223 lines
9.2 KiB
C++
223 lines
9.2 KiB
C++
// default c++ headers
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <array>
|
|
#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<std::string>(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;
|
|
}
|