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 + -
显示快捷键?