/* daapd 0.2.4, a server for the DAA protocol (c) deleet 2003, Alexander Oberdoerster type library daapd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. daapd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with daapd; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DAAP_TYPES_H #define DAAP_TYPES_H #include #include #include #include #include #include #include #include // #include "mmgr.h" #include #include #include "dboutput.h" #include "songcache.h" // STL defines no hash function for std::string. // How annoying. #ifdef __GNUC__ // gcc version < 3.1.0 ? #if __GNUC__ < 3 || \ (__GNUC__ == 3 && __GNUC_MINOR__ < 1) __STL_BEGIN_NAMESPACE template<> struct hash { size_t operator()(std::string __s) const { return __stl_hash_string( (const char*) __s.c_str() ); } }; __STL_END_NAMESPACE #else namespace __gnu_cxx { template<> struct hash { size_t operator()(std::string __s) const { return __stl_hash_string( (const char*) __s.c_str() ); } }; } #endif // GCC_VERSION #else __STL_BEGIN_NAMESPACE template<> struct hash { size_t operator()(std::string __s) const { return __stl_hash_string( (const char*) __s.c_str() ); } }; __STL_END_NAMESPACE #endif // __GNUC__ // database/server init parameters struct InitParams { u32 port; s32 timeScan; u8 verbose; u8 daemon; u8 quiet; u32 rescanInterval; u8 compression; std::string serverName; std::string dbName; std::string password; std::vector *dirs; std::string configFile; std::string cacheFile; InitParams() : port (3689), timeScan (3), verbose (0), daemon (0), quiet (0), rescanInterval (300), compression (1), serverName ("daapd server"), dbName ("daapd music"), password (""), cacheFile ("") { dirs = new std::vector; } ~InitParams() { free(dirs); } }; // meta filter bit field const u64 ITEMID = (u64) 1 << 1; const u64 ITEMNAME = (u64) 1 << 2; const u64 ITEMKIND = (u64) 1 << 3; const u64 PERSISTENTID = (u64) 1 << 4; const u64 SONGALBUM = (u64) 1 << 5; const u64 SONGARTIST = (u64) 1 << 6; const u64 SONGBITRATE = (u64) 1 << 7; const u64 SONGBEATSPERMINUTE = (u64) 1 << 8; const u64 SONGCOMMENT = (u64) 1 << 9; const u64 SONGCOMPILATION = (u64) 1 << 10; const u64 SONGCOMPOSER = (u64) 1 << 11; const u64 SONGDATEADDED = (u64) 1 << 12; const u64 SONGDATEMODIFIED = (u64) 1 << 13; const u64 SONGDISCCOUNT = (u64) 1 << 14; const u64 SONGDISCNUMBER = (u64) 1 << 15; const u64 SONGDISABLED = (u64) 1 << 16; const u64 SONGEQPRESET = (u64) 1 << 17; const u64 SONGFORMAT = (u64) 1 << 18; const u64 SONGGENRE = (u64) 1 << 19; const u64 SONGDESCRIPTION = (u64) 1 << 20; const u64 SONGRELATIVEVOLUME = (u64) 1 << 21; const u64 SONGSAMPLERATE = (u64) 1 << 22; const u64 SONGSIZE = (u64) 1 << 23; const u64 SONGSTARTTIME = (u64) 1 << 24; const u64 SONGSTOPTIME = (u64) 1 << 25; const u64 SONGTIME = (u64) 1 << 26; const u64 SONGTRACKCOUNT = (u64) 1 << 27; const u64 SONGTRACKNUMBER = (u64) 1 << 28; const u64 SONGUSERRATING = (u64) 1 << 29; const u64 SONGYEAR = (u64) 1 << 30; const u64 SONGDATAKIND = (u64) 1 << 31; const u64 SONGDATAURL = (u64) 1 << 32; const u64 NORMVOLUME = (u64) 1 << 33; const u64 SMARTPLAYLIST = (u64) 1 << 34; const u64 CONTAINERITEMID = (u64) 1 << 35; // file types const int DAAP_INVALID = 0; const int DAAP_DIRECTORY = 1; const int DAAP_MP3 = 2; const int DAAP_ID3 = 3; const int DAAP_AIFF = 4; const int DAAP_WAV = 5; const int DAAP_SD2 = 6; const int DAAP_M4A = 7; const int DAAP_M3U = 8; const int DAAP_PLS = 9; const int DAAP_LIBRARY_ID = 1; // session handling struct SessionType { u32 sessionId; // maybe we'll have to add something later }; class Sessions { private: std::vector sessionList; typedef std::vector::iterator SessionIterator; public: Sessions() {} bool isValid( const u32 sessionId ) { for(SessionIterator i = sessionList.begin(); i < sessionList.end(); i++) if ( i->sessionId == sessionId ) return true; return false; } bool erase( const u32 sessionId ) { for(SessionIterator i = sessionList.begin(); i < sessionList.end(); i++) if ( i->sessionId == sessionId ) sessionList.erase(i); return false; } Sessions& operator << ( const u32 sessionId ) { SessionType s; s.sessionId = sessionId; sessionList.push_back( s ); return( *this ); } private: Sessions( const Sessions& ); Sessions& operator = ( const Sessions& ); }; // daap request type enum RequestNumber { DAAP_NOREQUEST = 0, DAAP_SERVINFO = 1, DAAP_CONTCODES = 2, DAAP_LOGIN = 3, DAAP_LOGOUT = 4, DAAP_UPDATE = 5, DAAP_DATABASES = 6, DAAP_RESOLVE = 7, DAAP_BROWSE = 8, DAAP_ADMIN = 9 }; struct Request { RequestNumber number; u32 session; u32 revision; u32 delta; std::string type; u64 meta; std::string filter; std::string query; std::string index; Request() : number( DAAP_NOREQUEST ), session( 0 ), revision( 0 ), delta( 0 ), type( "" ), meta( 0 ), filter( "" ), query( "" ), index( "" ) {} private: Request( const Request& ); Request& operator = ( const Request& ); }; // song output declarations TagOutput& operator << ( TagOutput& out, const Song& song ); TagOutput& operator << ( TagOutput& out, const Filter& f ); // container item output declarations TagOutput& operator << ( TagOutput& out, const Filter< std::map >& f ); TagOutput& operator << ( TagOutput& out, const std::map items ); // song database struct Database { u32 id; std::string serverName; std::string dbName; u32 revision; u32 lastRevision; std::string authUser; std::string authPassword; std::vector *dirs; std::string cacheFile; s32 timeScan; u8 verbose; u8 quiet; u32 rescanInterval; u8 compression; pthread_mutex_t dbLock; Dictionary songs; Dictionary containers; void scan() { std::vector songlist; songs.collectPointers( songlist ); for( u32 i = 0; i < songlist.size(); ++i ) songlist[i]->present = false; std::vector containerlist; containers.collectPointers( containerlist ); for( u32 i = 0; i < containerlist.size(); ++i ) if( containerlist[i]->id != (u32) DAAP_LIBRARY_ID ) containerlist[i]->present = false; for( u32 i = 0; i < dirs->size(); ++i ) { // if root is a directory, scan recursively for mp3 files // else just add the single file int fileType = getFileType( (*dirs)[i] ); if( fileType == DAAP_DIRECTORY ) { addDirectory( (*dirs)[i] ); } else if( fileType == DAAP_MP3 || fileType == DAAP_AIFF || fileType == DAAP_WAV || fileType == DAAP_SD2 || fileType == DAAP_M4A ) { addRegularFile( (*dirs)[i], fileType ); } else if( fileType == DAAP_M3U || fileType == DAAP_PLS ) { addPlaylist( (*dirs)[i], fileType ); } } // remove all files that disappeared from the filesystem from the database songs.collectPointers( songlist ); pthread_mutex_lock(&dbLock); for( u32 i = 0; i < songlist.size(); ++i ) { if( songlist[i]->present == false ) { songs.erase( songlist[i]->path ); revision++; } } pthread_mutex_unlock(&dbLock); // remove all playlists that disappeared from the filesystem from the database containers.collectPointers( containerlist ); pthread_mutex_lock(&dbLock); for( u32 i = 0; i < containerlist.size(); ++i ) { if( containerlist[i]->present == false ) { containers.erase( containerlist[i]->path ); revision++; } } pthread_mutex_unlock(&dbLock); if( cacheFile != "" ) SongCache::save( cacheFile.c_str(), songs, verbose ); refreshContainers(); } Database( InitParams& initParams ) : id( 1 ), // only 1 db, so id is always 1 serverName( initParams.serverName ), dbName( initParams.dbName ), revision( 3 ), // first rev. is always 3 lastRevision( 3 ), authUser( "" ), // iTunes compatibility authPassword( initParams.password ), dirs( initParams.dirs ), cacheFile( initParams.cacheFile ), timeScan( initParams.timeScan ), verbose( initParams.verbose ), quiet( initParams.quiet ), rescanInterval( initParams.rescanInterval ), compression( initParams.compression ) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutex_init( &dbLock, &attr ); if( cacheFile != "" ) SongCache::load( cacheFile.c_str(), songs, verbose ); if( verbose ) printf("%d songs read from cache\n", songs.size() ); // clear the present flag on all entries std::vector songlist; songs.collectPointers( songlist ); for( u32 i = 0; i < songlist.size(); ++i ) songlist[i]->present = false; Container *cont = new Container; cont->id = DAAP_LIBRARY_ID; cont->name = "Library"; cont->present = true; pthread_mutex_lock(&dbLock); containers.add( cont, "Library" ); pthread_mutex_unlock(&dbLock); scan(); } private: void refreshContainers() { // container "Library" containing everything Container *cont = containers.findId( DAAP_LIBRARY_ID ); std::vector idList; songs.collectIds( idList ); pthread_mutex_lock(&dbLock); cont->items.clear(); for( u32 i = 0; i < idList.size(); ++i ) cont->items[i] = idList[i]; pthread_mutex_unlock(&dbLock); std::vector containerlist; containers.collectPointers( containerlist ); for( u32 i = 0; i < containerlist.size(); ++i ) { if( containerlist[i]->present == false || containerlist[i]->size() == 0 ) { if ( verbose ) printf( "erasing container %s\n", containerlist[i]->name.c_str() ); revision++; pthread_mutex_lock(&dbLock); containers.erase( containerlist[i]->path ); pthread_mutex_unlock(&dbLock); } } } int getId3TextFrame( id3_tag* tag, const char* frameId, std::string& out, bool allStrings ); int getId3Comment( id3_tag* tag, std::string& out); void addMp3( std::string& path, struct stat sb ); void addM4a( std::string& path, struct stat sb ); void addTagless( std::string& path, struct stat sb, int fileType); void addURL( std::string& path, int fileType); void parseM3u( Container &cont, bool fillPlaylist ); void parsePls( Container &cont, bool fillPlaylist ); int getTypeFromExtension( std::string& fileName ); int getFileType( std::string& fileName ); void addRegularFile( std::string& path, int fileType ); void addDirectory( std::string& path ); void addPlaylist( std::string& path, int fileType ); // declare copy constructor private // so we don't accidentally copy big structures (slow) Database( const Database& ); Database& operator = ( const Database& ); }; #endif