1 /// 2 /// PlayList / Artist / Album / Song types that are interbred 3 /// to form a library. 4 /// @file musiclibrary.h - pianod 5 /// @author Perette Barella 6 /// @date 2014-12-09 7 /// @copyright Copyright (c) 2014-2020 Devious Fish. All rights reserved. 8 /// 9 10 #pragma once 11 12 #include <config.h> 13 14 #include <cstdlib> 15 16 #include <string> 17 #include <unordered_map> 18 #include <unordered_set> 19 #include <vector> 20 #include <exception> 21 22 #include <parsnip.h> 23 24 #include "musictypes.h" 25 #include "musickeys.h" 26 #include "filter.h" 27 28 /// Memory-based index/database of music library contents. 29 namespace MusicLibrary { 30 class Playlist; 31 class Song; 32 33 const static int BIAS_MINIMUM = 1; 34 const static int BIAS_MAXIMUM = 100; 35 const static int BIAS_NEUTRAL = 1; ///< Neutral biasing factor for choosing songs 36 37 struct LibraryParameters; 38 39 class Foundation { 40 private: 41 mutable time_t write_time = 0; 42 protected: 43 virtual bool restoreIndexFromFile (const std::string &filename) = 0; 44 45 virtual bool writeIndexToFile (const std::string &filename) const = 0; persist(Parsnip::SerialData & into)46 virtual void persist (Parsnip::SerialData &into) const { }; restore(const Parsnip::SerialData & data)47 virtual bool restore (const Parsnip::SerialData &data) { return true; }; 48 public: 49 Media::Source *const source; 50 Foundation (Media::Source *owner); 51 typedef enum { 52 IMPORTANT = 300, 53 NOMINAL = 1800, 54 TRIVIAL = 60 * 60 * 6 55 } IMPORTANCE; 56 inline void markDirty (IMPORTANCE import = TRIVIAL) const { 57 time_t when = time (nullptr) + import; 58 if (write_time == 0 || when < write_time) { 59 write_time = when; 60 } 61 } 62 bool load(); 63 bool flush (); 64 float periodic (); 65 66 virtual SongList getAllSongs (void) = 0; 67 virtual SongList getMatchingSongs (const Filter &criteria) = 0; 68 virtual bool removePlaylist (Playlist *play) = 0; 69 virtual ThingieList seedsForPlaylist (const Playlist *playlist) = 0; 70 virtual void populatePlaylist (Playlist *play, bool aggressive = false) = 0; 71 virtual SongList getSongsForPlaylist (PianodPlaylist *) = 0; 72 virtual SongList getPlaylistSongs (const Playlist *play, bool reassess = false) = 0; 73 SongList getRandomSongs (PianodPlaylist *playlist, 74 const UserList &users, 75 Media::SelectionMethod selectionMethod, 76 const LibraryParameters &settings); 77 }; 78 79 80 /// A PianodPlaylist for music libraries. 81 class Playlist : public PianodPlaylist { 82 friend class TransientPlaylist; 83 private: 84 Foundation *const _library; 85 const std::string _id; 86 std::string _name; 87 mutable bool genres_dirty {true}; 88 mutable std::string _genres; 89 90 void calculateGenres() const; 91 public: 92 typedef std::unordered_set<std::string> SeedSet; 93 SeedSet seeds; 94 bool enabled = true; 95 Filter selector; ///< First selection of music from the library 96 public: Playlist(Foundation * const library,const std::string & id,const std::string & name)97 Playlist (Foundation *const library, const std::string &id, const std::string &name) 98 : _library (library), _id (id), _name (name) { }; 99 // Default constructor is not used, but necessary for TransientPlaylist to compile (note virtual base class). Playlist()100 Playlist (): Playlist (nullptr, "", "") { assert (!"Default constructor called for MusicLibrary::Playlist"); }; library(void)101 inline Foundation *const library (void) const { return _library; }; parent(void)102 inline Foundation *const parent (void) const { return _library; }; source(void)103 virtual Media::Source *const source (void) const override final { return _library->source; }; includedInMix(void)104 virtual bool includedInMix (void) const override { return enabled; }; includedInMix(bool include)105 virtual void includedInMix (bool include) override { enabled = include; }; 106 playlistType(void)107 virtual PlaylistType playlistType (void) const override { return SINGLE; }; playlistId(void)108 virtual const std::string &playlistId (void) const override { return _id; }; playlistName(void)109 virtual const std::string &playlistName (void) const override { return _name; }; 110 virtual const std::string &genre (void) const override; 111 112 virtual bool canSeed (MusicThingie::Type seedType) const override; 113 virtual bool seed (MusicThingie::Type seedType, const MusicThingie *music) const override; 114 virtual ThingieList getSeeds (void) const override; 115 virtual void seed (MusicThingie::Type seedType, MusicThingie *music, 116 bool value) override; 117 virtual SongList songs () override; 118 virtual SongList songs (const Filter &filter) override; 119 virtual void rename (const std::string &newname) override; 120 virtual void erase () override; 121 122 bool appliesTo (const PianodSong *song) const; 123 const std::string *getIdForSeed (MusicThingie::Type seedType, 124 const MusicThingie *music) const; 125 void invalidateSeeds (const MusicThingie *music); 126 virtual Parsnip::SerialData persist () const; 127 virtual void restore (const Parsnip::SerialData &data); 128 }; 129 130 class TransientPlaylist : public PianodTransientPlaylist <Playlist> { 131 public: 132 TransientPlaylist (Foundation *const library, const Filter &criteria); 133 virtual SongList songs () override; 134 }; 135 136 class Album; 137 class Song; 138 139 /// A PianodArtist that contains a vector of albums. 140 class Artist : public PianodArtist { 141 friend class Album; 142 friend class Song; 143 friend class Playlist; 144 private: 145 Foundation *const _library; 146 const std::string _id; 147 const std::string _name; 148 std::vector<Album *> albums; 149 public: 150 Artist (Foundation *const library, const std::string &id, const std::string &name); 151 virtual ~Artist (void) override; parent(void)152 inline Foundation *const parent (void) const { return _library; }; library(void)153 inline Foundation *const library (void) const { return _library; }; source(void)154 virtual Media::Source *const source (void) const override final { return _library->source; }; 155 artistId(void)156 virtual const std::string &artistId (void) const override { return _id; }; artist(void)157 virtual const std::string &artist (void) const override { return _name; }; 158 159 virtual SongList songs () override; 160 virtual Parsnip::SerialData persist () const; 161 virtual void restore (const Parsnip::SerialData &data); empty()162 inline bool empty() const { return albums.empty(); }; getAlbums()163 const std::vector<Album *> &getAlbums() { return albums; }; 164 }; 165 166 /// A PianodAlbum that contains a vector of songs 167 class Album : public PianodAlbum { 168 friend class Artist; 169 friend class Song; 170 friend class Playlist; 171 friend SongList Foundation::getRandomSongs (PianodPlaylist *playlist, 172 const UserList &users, 173 Media::SelectionMethod selectionMethod, 174 const LibraryParameters &settings); 175 protected: 176 Artist * _artist; 177 private: 178 const std::string _id; 179 const std::string _name; 180 std::vector<Song *> _songs; 181 std::string _coverArt; 182 mutable std::string _artists; 183 mutable bool artists_dirty = false; 184 public: 185 Album (Artist *const parent, const std::string &id, const std::string &name); 186 virtual ~Album (void) override; parent(void)187 inline Artist *const parent (void) const { return _artist; }; library(void)188 inline Foundation *const library (void) const { return _artist->_library; }; source(void)189 virtual Media::Source *const source (void) const override final { return _artist ->_library->source; }; 190 artistId(void)191 virtual const std::string &artistId (void) const override { return _artist->_id; }; 192 virtual const std::string &artist (void) const override; 193 albumId(void)194 virtual const std::string &albumId (void) const override { return _id; }; albumTitle(void)195 virtual const std::string &albumTitle (void) const override { return _name; }; coverArtUrl(void)196 virtual const std::string &coverArtUrl (void) const override { return _coverArt; }; 197 virtual bool compilation () const override; 198 coverArtUrl(const std::string & a)199 inline void coverArtUrl (const std::string &a) { _coverArt = a; }; 200 void calculateArtists (void) const; 201 virtual void compilation (Artist *compilation_artist); 202 203 virtual SongList songs () override; 204 virtual Parsnip::SerialData persist () const; 205 virtual void restore (const Parsnip::SerialData &data); empty()206 inline bool empty() const { return _songs.empty(); }; getSongs()207 const std::vector<Song *> &getSongs() { return _songs; }; 208 }; 209 210 /// A PianodSong made of inbred data structures. 211 class Song : public PianodSong { 212 friend class Playlist; 213 friend class Album; 214 friend SongList Foundation::getRandomSongs (PianodPlaylist *playlist, 215 const UserList &users, 216 Media::SelectionMethod selectionMethod, 217 const LibraryParameters &settings); 218 protected: 219 Album *const _album; 220 private: 221 const std::string _id; 222 const std::string _name; 223 224 Artist *_artist = nullptr; 225 std::string _genre; 226 int _duration = 0; 227 int _year = 0; 228 int _trackNumber = 0; 229 public: // For now 230 Playlist *_playlist = nullptr; 231 public: 232 Song (Album *const parent, const std::string &id, const std::string &name); 233 virtual ~Song (void) override; parent(void)234 inline Album *const parent (void) const { return _album; }; library(void)235 inline Foundation *const library (void) const { return _album->_artist->_library; }; source(void)236 virtual Media::Source *const source (void) const override final { return _album->_artist->_library->source; }; songId(void)237 virtual const std::string &songId (void) const override { return _id; }; 238 artistId(void)239 virtual const std::string &artistId (void) const override { return _album->_artist ->_id; }; 240 virtual const std::string &artist (void) const override; 241 albumId(void)242 virtual const std::string &albumId (void) const override { return _album->_id; }; albumTitle(void)243 virtual const std::string &albumTitle (void) const override { return _album->_name; }; coverArtUrl(void)244 virtual const std::string &coverArtUrl (void) const override { return _album->_coverArt; }; compilation()245 virtual bool compilation () const override { return _album->compilation(); }; 246 title(void)247 virtual const std::string &title (void) const override { return _name; }; 248 249 virtual PianodPlaylist *playlist (void) const override; genre(void)250 virtual const std::string &genre (void) const override { return _genre; } duration(void)251 virtual int duration (void) const override { return _duration; }; year(void)252 virtual int year (void) const override { return _year; }; trackNumber(void)253 virtual int trackNumber (void) const override {return _trackNumber; }; albumTrackCount()254 inline int albumTrackCount () const { return _album->_songs.size(); }; 255 256 void artist (Artist *artist); genre(const std::string & g)257 inline void genre (const std::string &g) { _genre = g; }; duration(int d)258 inline void duration (int d) { _duration = d; }; year(int y)259 inline void year (int y) { _year = y; }; trackNumber(int n)260 inline void trackNumber (int n) { _trackNumber = n; }; 261 262 virtual Parsnip::SerialData persist () const; 263 virtual void restore (const Parsnip::SerialData &data); 264 265 266 // Content management ratingScheme(void)267 virtual RatingScheme ratingScheme (void) const override { return RatingScheme::Individual; }; 268 virtual RESPONSE_CODE rate (Rating value, User *user) override; 269 virtual Rating rating (const User *user) const override; 270 virtual RESPONSE_CODE rateOverplayed (User *user) override; 271 }; 272 273 274 /// Customized hash tables for music library. 275 template <class TThing, class TParent> class ThingieContainer 276 : public std::unordered_map<std::string, TThing *> { 277 public: 278 ~ThingieContainer(); 279 280 void clear (); 281 void purge (bool pred(const TThing *)); 282 283 284 TThing *getById (const std::string &key) const; 285 TThing *getById (const Parsnip::SerialData &data, const char *field); 286 287 /** Search the things looking for a name and parent match. */ 288 TThing *getByName(const std::string &name, TParent *parent) const; 289 std::string getNewId (MusicThingie::Type item_type) const; 290 TThing *addItem (const std::string &name, std::string id, TParent *parent); 291 TThing *addOrGetItem (const std::string &name, std::string id, TParent *parent); addOrGetItem(const std::string & name,TParent * parent)292 inline TThing *addOrGetItem (const std::string &name, TParent *parent) { 293 return addOrGetItem (name, "", parent); 294 } 295 TThing *addOrGetItem (const Parsnip::SerialData &data, TParent *parent, 296 const std::string &namefield, const std::string &idfield); 297 }; 298 isCompilationAlbum(const Parsnip::SerialData & album)299 static inline bool isCompilationAlbum (const Parsnip::SerialData &album) { 300 return (album.contains (MusicStorage::AlbumIsCompilation) && 301 album [MusicStorage::AlbumIsCompilation].asBoolean()); 302 } 303 304 305 306 307 template <class TSong = Song, class TAlbum = Album, class TArtist = Artist> 308 class Library : public Foundation 309 { 310 protected: 311 typedef ThingieContainer <TArtist, Foundation> artist_container; 312 typedef ThingieContainer <TAlbum, TArtist> album_container; 313 typedef ThingieContainer <TSong, TAlbum> song_container; 314 typedef ThingieContainer <Playlist, Foundation> playlist_container; 315 artist_container artists; 316 album_container albums; 317 song_container songs; 318 playlist_container playlists; 319 Library(Media::Source * owner)320 Library (Media::Source *owner) 321 : Foundation (owner) { 322 // Derived classes should invoke restoreIndexFromFile; 323 // object is not fully constituted here. 324 }; 325 326 void purge (void); 327 328 public: 329 virtual bool removePlaylist (Playlist *play) override; 330 virtual ThingieList seedsForPlaylist (const Playlist *playlist) override; 331 SongList getAllSongs (void) override; 332 SongList getMatchingSongs (const Filter &criteria) override; 333 ThingieList getSuggestions (const Filter &criteria, SearchRange what); 334 SongList getMixSongs (void); 335 /** Get a list of all songs assigned to a playlist. 336 @param reassess If false, only assigned songs are returned. 337 If true, songs in other playlists are also considered. */ 338 virtual SongList getPlaylistSongs (const Playlist *play, bool reassess = false) override; 339 Playlist *findPlaylistForSong (TSong *song, bool enabled = true); 340 virtual void populatePlaylist (Playlist *play, bool aggressive = false) override; 341 void unpopulatePlaylist (Playlist *play); 342 /** Iterate over every song and replace its playlist assignment. 343 Applicable on initialization. */ mixRecalculate(void)344 void mixRecalculate (void) { 345 for (auto item : songs) { 346 item.second->_playlist = findPlaylistForSong (item.second); 347 } 348 } 349 MusicThingie *getById (MusicThingie::Type type, 350 const std::string &id); 351 virtual SongList getSongsForPlaylist (PianodPlaylist *playlist) override; 352 PianodPlaylist *createPlaylist (const std::string &name, 353 MusicThingie::Type type, 354 MusicThingie *from); 355 PianodPlaylist *createPlaylist (const std::string &name, const Filter &filter); 356 PianodPlaylist *formTransientPlaylist (const Filter &criteria); 357 358 virtual bool writeIndexToFile (const std::string &filename) const override; 359 virtual bool restoreIndexFromFile (const std::string &filename) override; 360 }; 361 }; 362 363