// default c++ headers #include #include #include #include #include #include "scanner.hh" #include "config.hh" #include "common.hh" #include "hathor_mysql.hh" #include "3rdparty/crypt_blowfish.h" namespace fs = boost::filesystem; using namespace std; static FileType fileType( std::string extension ) { for( unsigned int i = 1; i < s_fileTypeStrings.size(); i++ ) { if( extension.compare(s_fileTypeStrings.at( i )) == 0 ) return FileType( i ); } return FileType( Unknown ); } /* * hathor_handle_file( std::string file ) * Process file information and send it to MySQL */ static s_fileFields handle_file( std::string file ) { s_fileFields filefields; // Structure to store file information for MySQL fs::path current = fs::system_complete(file); // Get current file object std::string current_path = current.parent_path().native(); // Get current path minus the file TagLib::FileRef f( file.c_str() ); // Create TagLib object if(!f.isNull() && f.tag()) { filefields.artist = ( f.tag()->artist().isEmpty() ? "Unknown" : f.tag()->artist().toCString(true)); filefields.album = ( f.tag()->album().isEmpty() ? "Unknown" : f.tag()->album().toCString(true)); filefields.title = ( f.tag()->title().isEmpty() ? file.c_str() : f.tag()->title().toCString(true) ); filefields.genre = ( f.tag()->genre().isEmpty() ? "null" : f.tag()->genre().toCString(true)); filefields.comment = ( f.tag()->comment().isEmpty() ? "null" : f.tag()->comment().toCString(true)); filefields.tracknum = ( f.tag()->track() ? f.tag()->track() : 0 ); filefields.year = ( f.tag()->year() ? f.tag()->year() : 0 ); TagLib::AudioProperties *p = f.audioProperties(); filefields.bitrate = ( p->bitrate() ? p->bitrate() : 0 ); filefields.sample = ( p->sampleRate() ? p->sampleRate() : 0 ); filefields.channels = ( p->channels() ? p->channels() : 0 ); if( p->length() ) { filefields.length = p->length(); filefields.seconds = p->length() % 60; filefields.minutes = ( p->length() - filefields.seconds ) / 60; } else { filefields.length = 0; filefields.seconds = 0; filefields.minutes = 0; } TagLib::PropertyMap tags = f.file()->properties(); for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) { if(i->first == "ALBUMARTIST") { filefields.albumartist = (j->isEmpty() ? "null" : j->toCString(true)); } if(i->first == "LYRICS") { filefields.lyrics = (j->isEmpty() ? "null" : j->toCString(true)); } if(i->first == "LABEL") { filefields.label = (j->isEmpty() ? "null" : j->toCString(true));} } } ++file_count; } else { FILE * notagslog; notagslog = fopen("no_media_info.log", "a"); fprintf (notagslog, "Skipping %s, no media information found ...\n",file.c_str()); printf("Skipping %s, no media information found ...\n", file.c_str()); //notagslog << file << endl; ++no_info_count; fclose(notagslog); } return filefields; } json j = parse_file("config.json"); scanner_config s_opts = hathor_config(j); static int handle_directory( std::string path ) { sql::Connection *conn = get_connection( s_opts.mysql.user.c_str(), s_opts.mysql.pass.c_str(), s_opts.mysql.host.c_str(), s_opts.mysql.port ); conn->setAutoCommit(0); // Turn autocommit off for transactions. conn->setSchema(s_opts.mysql.db); unsigned int maxPstmt = get_max_statements(conn); sql::PreparedStatement *artist = conn->prepareStatement("INSERT IGNORE INTO artists (arid,name) VALUES(MD5(?),?)"); sql::PreparedStatement *album = conn->prepareStatement("INSERT IGNORE INTO albums (aid,name,arid,albumartist,label,released) VALUES(MD5(?),?,MD5(?),?,?,?)"); sql::PreparedStatement *track = conn->prepareStatement("INSERT IGNORE INTO tracks (tid,uri,arid,aid,title,track,genre,lyrics,comment,bitrate,sample,channels,length,minutes,seconds) VALUES(MD5(?),?,MD5(?),MD5(?),?,?,?,?,?,?,?,?,?,?,?)"); ofstream skipped; // Open file for adding skipped files skipped.open("skipped_files.log", ios::out | ios::app); // open log ofstream error_log; error_log.open("errors.log",ios::out|ios::app); fs::path p( fs::system_complete( path.c_str() ) ); // Get the current path we are working on cout << p.native() << endl; if ( !fs::exists(p) ) { // exit if path was not found std::cout << "\nPath Not found: " << p << std::endl; return 1; } fs::recursive_directory_iterator end_iter; for (fs::recursive_directory_iterator dir_itr(p); dir_itr != end_iter; ++dir_itr) { try { if ( fs::is_directory( dir_itr->status() ) && !is_hidden( dir_itr->path() ) ) { ++dir_count; printf(" * Scanning directory %s ...\n", dir_itr->path().native().c_str()); continue; } else if ( fs::is_regular_file( dir_itr->status() ) && !is_hidden( dir_itr->path() ) ) { std::string extension = dir_itr->path().extension().native(); if( !extension.empty() ) { extension.erase(extension.find('.'), extension.find('.')+1); toLower(extension); if( fileType(extension) == FileType( Unknown ) ) { std::cout << " * Skipping file " << dir_itr->path().native() << " ... \n"; skipped << "[" << extension << "] " << dir_itr->path().native() << "\n"; ++skip_count; } else { s_fileFields filefields = handle_file( dir_itr->path().native() ); char aid[1000]; std::string ayear = boost::lexical_cast(filefields.year); strcpy(aid,filefields.album.c_str()); strcat(aid,filefields.artist.c_str()); strcat(aid, ayear.c_str()); // Artist artist->setString(1, filefields.artist ); // artists.arid artist->setString(2, filefields.artist ); // artists.name artist->executeUpdate(); // Album album->setString(1, aid ); // albums.aid album->setString(2, filefields.album ); // albums.name album->setString(3, filefields.artist ); // albums.arid album->setString(4, filefields.albumartist ); // albums.albumartist album->setString(5, filefields.label ); // albums.label album->setInt (6, filefields.year ); // albums.released album->executeUpdate(); // Tracks track->setString(1, dir_itr->path().native() ); // tracks.tid track->setString(2, dir_itr->path().native() ); // tracks.uri track->setString(3, filefields.artist ); // tracks.arid track->setString(4, aid ); // tracks.aid track->setString(5, filefields.title ); // tracks.title track->setInt (6, filefields.tracknum ); // tracks.track track->setString(7, filefields.genre ); // tracks.genre track->setString(8, filefields.lyrics ); // tracks.lyrics track->setString(9, filefields.comment ); // tracks.comment track->setInt (10, filefields.bitrate ); // tracks.bitrate track->setInt (11, filefields.sample ); // tracks.sample track->setInt (12, filefields.channels ); // tracks.channels track->setInt (13, filefields.length ); // tracks.length track->setInt (14, filefields.minutes ); // tracks.minutes track->setInt (15, filefields.seconds ); // tracks.seconds track->executeUpdate(); } } else { //std::cout << " * Skipping file " << dir_itr->path().native() << " ... \n"; skipped << "[no extension] " << dir_itr->path().native() << "\n"; ++skip_count; } } else { skipped << "[other] " << dir_itr->path().native() << "\n"; ++other_count; } } catch (const sql::SQLException & sqlError ) { error_log << "[SQLError] : " << dir_itr->path().filename() << " " << sqlError.what() << std::endl; continue; } catch (const std::exception & ex) { ++err_count; std::cout << dir_itr->path().filename() << " " << ex.what() << std::endl; error_log << dir_itr->path().filename() << " " << ex.what() << std::endl; } } skipped.close(); error_log.close(); conn->commit(); delete artist; delete album; delete track; delete conn; return 0; } int main( int argc, char *argv[] ) { if(argc > 1) s_opts.scanner.path = fs::current_path().native(); handle_directory(s_opts.scanner.path); std::cout << "Directorys : " << dir_count << endl; std::cout << "Media : " << file_count << endl; std::cout << "No tags : " << no_info_count << endl; std::cout << "Skipped : " << skip_count << endl; std::cout << "Other Files : " << other_count << endl; std::cout << "Errors : " << err_count << endl; return 0; }