collectiondb.cpp
来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 1,901 行 · 第 1/5 页
CPP
1,901 行
// (c) 2004 Mark Kretschmann <markey@web.de>// (c) 2004 Christian Muehlhaeuser <chris@chris.de>// (c) 2004 Sami Nieminen <sami.nieminen@iki.fi>// (c) 2005 Ian Monroe <ian@monroe.nu>// (c) 2005 Jeff Mitchell <kde-dev@emailgoeshere.com>// (c) 2005 Isaiah Damron <xepo@trifault.net>// (c) 2005-2006 Alexandre Pereira de Oliveira <aleprj@gmail.com>// (c) 2006 Jonas Hurrelmann <j@outpo.st>// (c) 2006 Shane King <kde@dontletsstart.com>// (c) 2006 Peter C. Ndikuwera <pndiku@gmail.com>// (c) 2006 Stanislav Nikolov <valsinats@gmail.com>// See COPYING file for licensing information.#define DEBUG_PREFIX "CollectionDB"#include "app.h"#include "amarok.h"#include "amarokconfig.h"#include "config.h"#include "debug.h"#include "collectionbrowser.h" //updateTags()#include "collectiondb.h"#include "coverfetcher.h"#include "enginecontroller.h"#include "expression.h"#include "mediabrowser.h"#include "metabundle.h" //updateTags()#include "mountpointmanager.h" //buildQuery()#include "organizecollectiondialog.h"#include "playlist.h"#include "playlistloader.h"#include "playlistbrowser.h"#include "podcastbundle.h" //addPodcast#include "qstringx.h"#include "scancontroller.h"#include "scriptmanager.h"#include "scrobbler.h"#include "statusbar.h"#include "threadmanager.h"#include <qbuffer.h>#include <qcheckbox.h>#include <qdeepcopy.h>#include <qfile.h>#include <qmap.h>#include <qmutex.h>#include <qregexp.h> //setHTMLLyrics()#include <qtimer.h>#include <qpainter.h> //createDragPixmap()#include <qpalette.h>#include <pthread.h> //debugging, can be removed later#include <kcharsets.h> //setHTMLLyrics()#include <kcombobox.h>#include <kconfig.h>#include <kdialogbase.h> //checkDatabase()#include <kglobal.h>#include <kinputdialog.h> //setupCoverFetcher()#include <klineedit.h> //setupCoverFetcher()#include <klocale.h>#include <kmdcodec.h>#include <kmessagebox.h>#include <ksimpleconfig.h>#include <kstandarddirs.h>#include <kio/job.h>#include <kio/netaccess.h>#include <cmath> //DbConnection::sqlite_power()#include <ctime> //query()#include <cstdlib> //exit()#include <unistd.h> //usleep()#include <taglib/audioproperties.h>#include "sqlite/sqlite3.h"#ifdef USE_MYSQL #include <mysql/mysql.h> #include <mysql/mysql_version.h>#endif#ifdef USE_POSTGRESQL #include <libpq-fe.h>#endif#undef HAVE_INOTIFY // NOTE Disabled for now, due to stability issues#ifdef HAVE_INOTIFY #include <linux/inotify.h> #include "inotify/inotify-syscalls.h"#endifusing Amarok::QStringx;#define DEBUG 0//////////////////////////////////////////////////////////////////////////////////////////// CLASS INotify//////////////////////////////////////////////////////////////////////////////////////////INotify* INotify::s_instance = 0;INotify::INotify( CollectionDB *parent, int fd ) : DependentJob( parent, "INotify" ) , m_parent( parent ) , m_fd( fd ){ s_instance = this;}INotify::~INotify(){}boolINotify::watchDir( const QString directory ){#ifdef HAVE_INOTIFY int wd = inotify_add_watch( m_fd, directory.local8Bit(), IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_MODIFY | IN_ATTRIB ); if ( wd < 0 ) debug() << "Could not add INotify watch for: " << directory << endl; return ( wd >= 0 );#else Q_UNUSED(directory);#endif return false;}boolINotify::doJob(){#ifdef HAVE_INOTIFY DEBUG_BLOCK IdList list = MountPointManager::instance()->getMountedDeviceIds(); QString deviceIds; foreachType( IdList, list ) { if ( !deviceIds.isEmpty() ) deviceIds += ','; deviceIds += QString::number(*it); } const QStringList values = m_parent->query( QString( "SELECT dir, deviceid FROM directories WHERE deviceid IN (%1);" ) .arg( deviceIds ) ); foreach( values ) { QString rpath = *it; int deviceid = (*(++it)).toInt(); QString abspath = MountPointManager::instance()->getAbsolutePath( deviceid, rpath ); watchDir( abspath ); } /* size of the event structure, not counting name */ const int EVENT_SIZE = ( sizeof( struct inotify_event ) ); /* reasonable guess as to size of 1024 events */ const int BUF_LEN = 1024 * ( EVENT_SIZE + 16 ); while ( 1 ) { char buf[BUF_LEN]; int len, i = 0; len = read( m_fd, buf, BUF_LEN ); if ( len < 0 ) { debug() << "Read from INotify failed" << endl; return false; } else { if ( !len ) { /* BUF_LEN too small? */ } else { while ( i < len ) { struct inotify_event *event; event = (struct inotify_event *) &buf[i]; i += EVENT_SIZE + event->len; } QTimer::singleShot( 0, m_parent, SLOT( scanMonitor() ) ); } } }#endif // this shouldn't happen return false;}//////////////////////////////////////////////////////////////////////////////////////////// CLASS CollectionDB//////////////////////////////////////////////////////////////////////////////////////////QMutex* CollectionDB::connectionMutex = new QMutex();QMutex* CollectionDB::itemCoverMapMutex = new QMutex();//we don't have to worry about this map leaking memory since ThreadManager limits the total//number of QThreads ever createdQMap<QThread *, DbConnection *> *CollectionDB::threadConnections = new QMap<QThread *, DbConnection *>();QMap<QListViewItem*, CoverFetcher*> *CollectionDB::itemCoverMap = new QMap<QListViewItem*, CoverFetcher*>();CollectionDB* CollectionDB::instance(){ static CollectionDB db; return &db;}CollectionDB::CollectionDB() : EngineObserver( EngineController::instance() ) , m_autoScoring( true ) , m_noCover( locate( "data", "amarok/images/nocover.png" ) ) , m_shadowImage( locate( "data", "amarok/images/shadow_albumcover.png" ) ) , m_scanInProgress( false ) , m_rescanRequired( false ) , m_aftEnabledPersistentTables() , m_moveFileJobCancelled( false ){ DEBUG_BLOCK#ifdef USE_MYSQL if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) m_dbConnType = DbConnection::mysql; else#endif#ifdef USE_POSTGRESQL if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) m_dbConnType = DbConnection::postgresql; else#endif m_dbConnType = DbConnection::sqlite; //perform all necessary operations to allow MountPointManager to access the devices table here //there is a recursive dependency between CollectionDB and MountPointManager and this is the workaround //checkDatabase has to be able to access MountPointManager //<OPEN DATABASE> initialize(); //</OPEN DATABASE> // Remove cached "nocover" images, so that a new version actually gets shown // The asterisk is for also deleting the shadow-caches. const QStringList entryList = cacheCoverDir().entryList( "*nocover.png*", QDir::Files ); foreach( entryList ) cacheCoverDir().remove( *it ); connect( this, SIGNAL(fileMoved(const QString&, const QString&, const QString&)), this, SLOT(aftMigratePermanentTablesUrl(const QString&, const QString&, const QString&)) ); connect( this, SIGNAL(uniqueIdChanged(const QString&, const QString&, const QString&)), this, SLOT(aftMigratePermanentTablesUniqueId(const QString&, const QString&, const QString&)) ); connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( disableAutoScoring() ) ); connect( this, SIGNAL( coverRemoved( const QString&, const QString& ) ), SIGNAL( coverChanged( const QString&, const QString& ) ) ); connect( Scrobbler::instance(), SIGNAL( similarArtistsFetched( const QString&, const QStringList& ) ), this, SLOT( similarArtistsFetched( const QString&, const QStringList& ) ) ); // If we're supposed to monitor dirs for changes, make sure we run it once // on startup, since inotify can't inform us about old events// QTimer::singleShot( 0, this, SLOT( scanMonitor() ) ) initDirOperations(); m_aftEnabledPersistentTables << "lyrics" << "statistics" << "tags_labels";}CollectionDB::~CollectionDB(){ DEBUG_BLOCK#ifdef HAVE_INOTIFY if ( INotify::instance()->fd() >= 0 ) close( INotify::instance()->fd() );#endif destroy();}inline QStringCollectionDB::exactCondition( const QString &right ){ if ( DbConnection::mysql == instance()->getDbConnectionType() ) return QString( "= BINARY '" + instance()->escapeString( right ) + '\'' ); else return QString( "= '" + instance()->escapeString( right ) + '\'' );}QStringCollectionDB::likeCondition( const QString &right, bool anyBegin, bool anyEnd ){ QString escaped = right; escaped.replace( '/', "//" ).replace( '%', "/%" ).replace( '_', "/_" ); escaped = instance()->escapeString( escaped ); QString ret; if ( DbConnection::postgresql == instance()->getDbConnectionType() ) ret = " ILIKE "; //case-insensitive according to locale else ret = " LIKE "; ret += '\''; if ( anyBegin ) ret += '%'; ret += escaped; if ( anyEnd ) ret += '%'; ret += '\''; //Use / as the escape character ret += " ESCAPE '/' "; return ret;}//////////////////////////////////////////////////////////////////////////////////////////// PUBLIC//////////////////////////////////////////////////////////////////////////////////////////voidCollectionDB::initDirOperations(){ //this code was originally part of the ctor. It has to call MountPointManager to //generate absolute paths from deviceids and relative paths. MountPointManager's ctor //absolutely has to access the database, which resulted in a recursive ctor call. To //solve this problem, the directory access code was moved into its own method, which can //only be called when the CollectionDB object already exists. //FIXME max: make sure we check additional directories if we connect a new device#ifdef HAVE_INOTIFY // Try to initialize inotify, if not available use the old timer approach. int inotify_fd = inotify_init(); if ( inotify_fd < 0 )#endif {// debug() << "INotify not available, using QTimer!" << endl; startTimer( MONITOR_INTERVAL * 1000 ); }#ifdef HAVE_INOTIFY else { debug() << "INotify enabled!" << endl; ThreadManager::instance()->onlyOneJob( new INotify( this, inotify_fd ) ); }#endif}/** * Executes a SQL query on the already opened database * @param statement SQL program to execute. Only one SQL statement is allowed. * @return The queried data, or QStringList() on error. */QStringListCollectionDB::query( const QString& statement, bool suppressDebug ){ m_mutex.lock(); clock_t start; if ( DEBUG ) { debug() << "Query-start: " << statement << endl; start = clock(); } if ( statement.stripWhiteSpace().isEmpty() ) { m_mutex.unlock(); return QStringList(); } DbConnection *dbConn;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?