1 /*
2 Vimpc
3 Copyright (C) 2010 Nathan Sweetman
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 3 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, see <http://www.gnu.org/licenses/>.
17
18 library.cpp - handling of the mpd music library
19 */
20
21 #include "library.hpp"
22
23 #include "algorithm.hpp"
24 #include "browse.hpp"
25 #include "clientstate.hpp"
26 #include "directory.hpp"
27 #include "events.hpp"
28 #include "mpdclient.hpp"
29 #include "playlist.hpp"
30 #include "vimpc.hpp"
31
32 #include <algorithm>
33
34 const std::string VariousArtist = "Various Artists";
35
36 using namespace Mpc;
37
Library()38 Library::Library() :
39 settings_ (Main::Settings::Instance()),
40 variousArtist_ (NULL),
41 lastAlbumEntry_ (NULL),
42 lastArtistEntry_(NULL)
43 {
44 AddCallback(Main::Buffer_Remove, [this] (LibraryEntry * const entry) { CheckIfVariousRemoved(entry); });
45
46 Main::Vimpc::EventHandler(Event::AllMetaDataReady, [this] (EventData const & Data) { Sort(); });
47
48 settings_.RegisterCallback(Setting::AlbumArtist, [this] (bool Value)
49 {
50 // Clear the cached format prints for every song
51 for (auto song : uriMap_)
52 {
53 song.second->FormatString("");
54 }
55
56 RecreateLibraryFromURIs();
57 });
58 }
59
~Library()60 Library::~Library()
61 {
62 Clear();
63 }
64
Clear(bool Delete)65 void Library::Clear(bool Delete)
66 {
67 lastAlbumEntry_ = NULL;
68 lastArtistEntry_ = NULL;
69
70 Main::Playlist().Clear();
71
72 // \todo investigate this, adds considerable overhead in callgrind
73 Main::Browse().Clear();
74
75 uriMap_.clear();
76
77 while (Size() > 0)
78 {
79 int const Pos = Size() - 1;
80 LibraryEntry * entry = Get(Pos);
81 Remove(Pos, 1);
82
83 if ((Delete == true) && (entry->parent_ == NULL))
84 {
85 delete entry;
86 }
87 }
88 }
89
RecreateLibraryFromURIs()90 void Library::RecreateLibraryFromURIs()
91 {
92 // collapse everything
93 ForEachParent([] (Mpc::LibraryEntry * entry) { Mpc::MarkUnexpanded(entry); });
94
95 // clear the songs
96 FUNCTION<void (LibraryEntry *)> function = [this] (LibraryEntry * entry) { entry->song_ = NULL; };
97
98 for (int i = 0; i < Size(); ++i)
99 {
100 ForEachChild(i, function);
101 }
102
103 // get rid of every entry
104 while (Size() > 0)
105 {
106 int const Pos = Size() - 1;
107 LibraryEntry * entry = Get(Pos);
108 Remove(Pos, 1);
109
110 if (entry->parent_ == NULL)
111 {
112 delete entry;
113 }
114 }
115
116 for (auto song : uriMap_)
117 {
118 Add(song.second);
119 }
120 }
121
Add(Mpc::Song * song)122 void Library::Add(Mpc::Song * song)
123 {
124 std::string artist = song->Artist();
125 std::string const album = song->Album();
126 std::string const albumartist = song->AlbumArtist();
127
128 if ((settings_.Get(Setting::AlbumArtist) == true) && (albumartist != "Unknown Artist"))
129 {
130 artist = albumartist;
131 }
132
133 CreateVariousArtist();
134
135 if ((lastAlbumEntry_ == NULL) ||
136 (Algorithm::iequals(lastAlbumEntry_->album_, album,
137 settings_.Get(Setting::IgnoreTheGroup), true) == false))
138 {
139 lastAlbumEntry_ = NULL;
140
141 if ((lastArtistEntry_ == NULL) ||
142 (Algorithm::iequals(lastArtistEntry_->artist_, artist) == false))
143 {
144 lastArtistEntry_ = NULL;
145
146 uint32_t i = 0;
147
148 for (i = 0;
149 ((i < Size()) &&
150 ((Get(i)->type_ != Mpc::ArtistType) ||
151 (Algorithm::iequals(Get(i)->artist_, artist,
152 settings_.Get(Setting::IgnoreTheGroup), true) == false)));
153 ++i);
154
155 if (i < Size())
156 {
157 lastArtistEntry_ = Get(i);
158 }
159 else
160 {
161 lastArtistEntry_ = CreateArtistEntry(artist);
162 }
163 }
164
165 for (auto entry : lastArtistEntry_->children_)
166 {
167 if ((entry->type_ == Mpc::AlbumType) &&
168 (Algorithm::iequals(entry->album_, album) == true))
169 {
170 lastAlbumEntry_ = entry;
171 break;
172 }
173 }
174
175 if (lastAlbumEntry_ == NULL)
176 {
177 lastAlbumEntry_ = CreateAlbumEntry(song);
178 lastAlbumEntry_->parent_ = lastArtistEntry_;
179 lastArtistEntry_->children_.push_back(lastAlbumEntry_);
180 }
181 }
182
183 if ((lastArtistEntry_ != NULL) && (lastAlbumEntry_ != NULL) &&
184 (lastArtistEntry_ != variousArtist_) &&
185 (lastArtistEntry_->children_.back() == lastAlbumEntry_) &&
186 (Algorithm::iequals(lastAlbumEntry_->album_, album) == true) &&
187 (Algorithm::iequals(lastArtistEntry_->artist_, artist,
188 settings_.Get(Setting::IgnoreTheGroup), true) == false))
189 {
190 lastArtistEntry_->children_.pop_back();
191
192 if (lastArtistEntry_->children_.size() == 0)
193 {
194 Remove(Index(lastArtistEntry_), 1);
195 delete lastArtistEntry_;
196 }
197
198 if (lastAlbumEntry_->parent_ != variousArtist_)
199 {
200 variousArtist_->children_.push_back(lastAlbumEntry_);
201 }
202
203 lastAlbumEntry_->parent_ = variousArtist_;
204 lastArtistEntry_ = variousArtist_;
205 }
206
207 Mpc::LibraryEntry * const entry = new Mpc::LibraryEntry();
208 entry->expanded_ = true;
209 entry->artist_ = artist;
210 entry->album_ = album;
211 entry->song_ = song;
212 entry->type_ = Mpc::SongType;
213 entry->parent_ = lastAlbumEntry_;
214 song->SetEntry(entry);
215
216 uriMap_[song->URI()] = song;
217
218 if (lastAlbumEntry_ != NULL)
219 {
220 lastAlbumEntry_->children_.push_back(entry);
221 }
222 }
223
CreateVariousArtist()224 void Library::CreateVariousArtist()
225 {
226 if (variousArtist_ == NULL)
227 {
228 variousArtist_ = new Mpc::LibraryEntry();
229 variousArtist_->expanded_ = false;
230 variousArtist_->artist_ = VariousArtist;
231 variousArtist_->type_ = Mpc::ArtistType;
232 Add(variousArtist_);
233 }
234 }
235
CreateArtistEntry(std::string artist)236 Mpc::LibraryEntry * Library::CreateArtistEntry(std::string artist)
237 {
238 Mpc::LibraryEntry * const entry = new Mpc::LibraryEntry();
239
240 entry->expanded_ = false;
241 entry->artist_ = artist;
242 entry->type_ = Mpc::ArtistType;
243
244 Add(entry);
245 return entry;
246 }
247
CreateAlbumEntry(Mpc::Song * song)248 Mpc::LibraryEntry * Library::CreateAlbumEntry(Mpc::Song * song)
249 {
250 Mpc::LibraryEntry * const entry = new Mpc::LibraryEntry();
251 entry->expanded_ = false;
252 entry->artist_ = song->Artist();
253 entry->album_ = song->Album();
254 entry->type_ = Mpc::AlbumType;
255 return entry;
256 }
257
Song(std::string uri) const258 Mpc::Song * Library::Song(std::string uri) const
259 {
260 std::map<std::string, Mpc::Song *>::const_iterator it = uriMap_.find(uri);
261
262 if (it != uriMap_.end())
263 {
264 return it->second;
265 }
266
267 return NULL;
268 }
269
270
Sort()271 void Library::Sort()
272 {
273 Mpc::LibraryEntry::LibraryComparator comparator;
274
275 Main::Buffer<Library::BufferType>::Sort(comparator);
276
277 for (uint32_t i = 0; (i < Size()); ++i)
278 {
279 Sort(Get(i));
280 }
281 }
282
Sort(LibraryEntry * entry)283 void Library::Sort(LibraryEntry * entry)
284 {
285 Mpc::LibraryEntry::LibraryComparator entryComparator;
286
287 if (entry->children_.empty() == false)
288 {
289 std::sort(entry->children_.begin(), entry->children_.end(), entryComparator);
290
291 for (auto child : entry->children_)
292 {
293 if (child->children_.empty() == false)
294 {
295 Sort(child);
296 }
297 }
298 }
299 }
300
AddToPlaylist(Mpc::Song::SongCollection Collection,Mpc::Client & client,Mpc::ClientState & clientState,uint32_t position)301 void Library::AddToPlaylist(Mpc::Song::SongCollection Collection, Mpc::Client & client, Mpc::ClientState & clientState, uint32_t position)
302 {
303 if (position < Size())
304 {
305 if (Collection == Mpc::Song::Single)
306 {
307 AddToPlaylist(client, clientState, Get(position));
308 }
309 else
310 {
311 Mpc::CommandList list(client);
312
313 for (uint32_t i = 0; i < Size(); ++i)
314 {
315 AddToPlaylist(client, clientState, Get(i));
316 }
317 }
318 }
319 }
320
RemoveFromPlaylist(Mpc::Song::SongCollection Collection,Mpc::Client & client,Mpc::ClientState & clientState,uint32_t position)321 void Library::RemoveFromPlaylist(Mpc::Song::SongCollection Collection, Mpc::Client & client, Mpc::ClientState & clientState, uint32_t position)
322 {
323 if (position < Size())
324 {
325 if (Collection == Mpc::Song::Single)
326 {
327 RemoveFromPlaylist(client, Get(position));
328 }
329 else
330 {
331 Mpc::CommandList list(client);
332
333 for (uint32_t i = 0; i < Size(); ++i)
334 {
335 RemoveFromPlaylist(client, Get(i));
336 }
337 }
338 }
339 }
340
AddToPlaylist(Mpc::Client & client,Mpc::ClientState & clientState,Mpc::LibraryEntry const * const entry,int32_t position)341 void Library::AddToPlaylist(Mpc::Client & client, Mpc::ClientState & clientState, Mpc::LibraryEntry const * const entry, int32_t position)
342 {
343 if ((entry->type_ == Mpc::SongType) && (entry->song_ != NULL))
344 {
345 if (position != -1)
346 {
347 client.Add(*(entry->song_), position);
348 }
349 else if ((Main::Settings::Instance().Get(Setting::AddPosition) == Setting::AddEnd) ||
350 (clientState.GetCurrentSongPos() == -1))
351 {
352 client.Add(*(entry->song_));
353 }
354 else
355 {
356 client.Add(*(entry->song_), clientState.GetCurrentSongPos() + 1);
357 }
358 }
359 else
360 {
361 int current = -1;
362
363 if ((Main::Settings::Instance().Get(Setting::AddPosition) == Setting::AddNext) &&
364 (clientState.GetCurrentSongPos() != -1))
365 {
366 current = clientState.GetCurrentSongPos() + 1;
367 }
368
369 for (auto child : entry->children_)
370 {
371 AddToPlaylist(client, clientState, child, current);
372
373 if (current != -1)
374 {
375 current++;
376 }
377 }
378 }
379 }
380
RemoveFromPlaylist(Mpc::Client & client,Mpc::LibraryEntry const * const entry)381 void Library::RemoveFromPlaylist(Mpc::Client & client, Mpc::LibraryEntry const * const entry)
382 {
383 if ((entry->type_ == Mpc::SongType) && (entry->song_ != NULL))
384 {
385 int32_t PlaylistIndex = Main::Playlist().Index(entry->song_);
386
387 if (PlaylistIndex >= 0)
388 {
389 client.Delete(PlaylistIndex);
390 }
391 }
392 else
393 {
394 for (auto child : entry->children_)
395 {
396 RemoveFromPlaylist(client, child);
397 }
398 }
399 }
400
ForEachChild(uint32_t index,FUNCTION<void (Mpc::Song *)> callback) const401 void Library::ForEachChild(uint32_t index, FUNCTION<void (Mpc::Song *)> callback) const
402 {
403 for (auto child : Get(index)->children_)
404 {
405 if (child->type_ == AlbumType)
406 {
407 for (auto child2 : child->children_)
408 {
409 (callback)(child2->song_);
410 }
411 }
412 else if (child->type_ == SongType)
413 {
414 (callback)(child->song_);
415 }
416 }
417 }
418
ForEachChild(uint32_t index,FUNCTION<void (Mpc::LibraryEntry *)> callback) const419 void Library::ForEachChild(uint32_t index, FUNCTION<void (Mpc::LibraryEntry *)> callback) const
420 {
421 for (auto child : Get(index)->children_)
422 {
423 if (child->type_ == AlbumType)
424 {
425 for (auto child2 : child->children_)
426 {
427 (callback)(child2);
428 }
429 }
430
431 (callback)(child);
432 }
433 }
434
ForEachSong(FUNCTION<void (Mpc::Song *)> callback) const435 void Library::ForEachSong(FUNCTION<void (Mpc::Song *)> callback) const
436 {
437 for (uint32_t i = 0; i < Size(); ++i)
438 {
439 if (Get(i)->type_ == ArtistType)
440 {
441 for (auto child : Get(i)->children_)
442 {
443 if (child->type_ == AlbumType)
444 {
445 for (auto child2 : child->children_)
446 {
447 (callback)(child2->song_);
448 }
449 }
450 }
451 }
452 }
453 }
454
ForEachParent(FUNCTION<void (Mpc::LibraryEntry *)> callback) const455 void Library::ForEachParent(FUNCTION<void (Mpc::LibraryEntry *)> callback) const
456 {
457 for (uint32_t i = 0; i < Size(); ++i)
458 {
459 if (Get(i)->type_ == ArtistType)
460 {
461 for (auto child : Get(i)->children_)
462 {
463 if (child->type_ == AlbumType)
464 {
465 (callback)(child);
466 }
467 }
468
469 (callback)(Get(i));
470 }
471 }
472 }
473
474
Expand(uint32_t line)475 void Library::Expand(uint32_t line)
476 {
477 uint32_t position = line;
478
479 if ((Get(line)->expanded_ == false) && (Get(line)->type_ != Mpc::SongType))
480 {
481 Get(line)->expanded_ = true;
482
483 for (auto child : Get(line)->children_)
484 {
485 Add(child, ++position);
486 }
487 }
488 }
489
Collapse(uint32_t line)490 void Library::Collapse(uint32_t line)
491 {
492 Mpc::LibraryEntry * const entry = Get(line);
493 Mpc::LibraryEntry * const parent = entry->parent_;
494 Mpc::LibraryEntry * entryToCollapse = entry;
495
496 if ((entry->expanded_ == false) || (entry->type_ == Mpc::SongType))
497 {
498 entryToCollapse = parent;
499 }
500
501 if (Index(entryToCollapse) >= 0)
502 {
503 uint32_t Pos = Index(entryToCollapse);
504
505 // Separated function out into variable as compile fails on g++ 4.7.2
506 // if passed directly to function using the lambda
507 FUNCTION<void (LibraryEntry *)> function = [this] (LibraryEntry * exp) { RemoveAndUnexpand(exp); };
508 ForEachChild(Pos, function);
509 entryToCollapse->expanded_ = false;
510 }
511 }
512
513
String(uint32_t position) const514 std::string Library::String(uint32_t position) const
515 {
516 Mpc::EntryType const type = Get(position)->type_;
517
518 std::string Result = "";
519
520 if (type == Mpc::ArtistType)
521 {
522 Result = Get(position)->artist_;
523 }
524 else if (type == Mpc::AlbumType)
525 {
526 Result = Get(position)->album_;
527 }
528 else if (type == Mpc::SongType)
529 {
530 Result = Get(position)->song_->FormatString(settings_.Get(Setting::LibraryFormat));
531 }
532
533 return Result;
534 }
535
PrintString(uint32_t position) const536 std::string Library::PrintString(uint32_t position) const
537 {
538 Mpc::EntryType const type = Get(position)->type_;
539
540 std::string Result = "";
541
542 if (type == Mpc::ArtistType)
543 {
544 std::string artist(Get(position)->artist_);
545 std::string const find = "$";
546 std::string const replace = "\\$";
547 for(std::string::size_type i = 0; (i = artist.find(find, i)) != std::string::npos;)
548 {
549 artist.replace(i, find.length(), replace);
550 i += replace.length();
551 }
552 Result = "$B " + artist + "$R$B";
553 }
554 else if (type == Mpc::AlbumType)
555 {
556 Result = " " + Get(position)->album_;
557 }
558 else if (type == Mpc::SongType)
559 {
560 Result = " " + Get(position)->song_->FormatString(settings_.Get(Setting::LibraryFormat));
561 }
562
563 return Result;
564 }
565
566
RemoveAndUnexpand(LibraryEntry * const entry)567 void Library::RemoveAndUnexpand(LibraryEntry * const entry)
568 {
569 if (Index(entry) != -1)
570 {
571 Remove(Index(entry), 1);
572 entry->expanded_ = false;
573 }
574 }
575
576
CheckIfVariousRemoved(LibraryEntry * const entry)577 void Library::CheckIfVariousRemoved(LibraryEntry * const entry)
578 {
579 if (entry == variousArtist_)
580 {
581 variousArtist_ = NULL;
582 }
583
584 if (entry == lastArtistEntry_)
585 {
586 lastArtistEntry_ = NULL;
587 }
588
589 if (entry == lastAlbumEntry_)
590 {
591 lastAlbumEntry_ = NULL;
592 lastArtistEntry_ = NULL;
593 }
594 }
595
596
MarkUnexpanded(LibraryEntry * const entry)597 void Mpc::MarkUnexpanded(LibraryEntry * const entry)
598 {
599 entry->expanded_ = false;
600 }
601
602 /* vim: set sw=3 ts=3: */
603