File indexing completed on 2024-06-16 07:42:26

0001 /*
0002     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bmediacache.h"
0007 #include "k3bmediacache_p.h"
0008 #include "k3bmedium.h"
0009 #include "k3bmedium_p.h"
0010 #include "k3bcddb.h"
0011 #include "k3bdevicemanager.h"
0012 #include "k3bdeviceglobals.h"
0013 #include "k3bcore.h"
0014 #include "k3b_i18n.h"
0015 
0016 #include <QDebug>
0017 #include <QThread>
0018 #include <QMutex>
0019 #include <QEvent>
0020 #include <QRandomGenerator>
0021 
0022 #include <KCDDB/Client>
0023 
0024 
0025 
0026 K3b::MediaCache::DeviceEntry::DeviceEntry( K3b::MediaCache* c, K3b::Device::Device* dev )
0027     : medium(dev),
0028       blockedId(0),
0029       cache(c)
0030 {
0031     thread = new K3b::MediaCache::PollThread( this );
0032     connect( thread, SIGNAL(mediumChanged(K3b::Device::Device*)),
0033              c, SLOT(_k_mediumChanged(K3b::Device::Device*)),
0034              Qt::QueuedConnection );
0035     connect( thread, SIGNAL(checkingMedium(K3b::Device::Device*,QString)),
0036              c, SIGNAL(checkingMedium(K3b::Device::Device*,QString)),
0037              Qt::QueuedConnection );
0038 }
0039 
0040 
0041 K3b::MediaCache::DeviceEntry::~DeviceEntry()
0042 {
0043     delete thread;
0044 }
0045 
0046 
0047 void K3b::MediaCache::PollThread::run()
0048 {
0049     while( m_deviceEntry->blockedId == 0 ) {
0050         bool unitReady = m_deviceEntry->medium.device()->testUnitReady();
0051         bool mediumCached = ( m_deviceEntry->medium.diskInfo().diskState() != K3b::Device::STATE_NO_MEDIA );
0052 
0053         //
0054         // we only get the other information in case the disk state changed or if we have
0055         // no info at all (FIXME: there are drives around that are not able to provide a proper
0056         // disk state)
0057         //
0058         if( m_deviceEntry->medium.diskInfo().diskState() == K3b::Device::STATE_UNKNOWN ||
0059             unitReady != mediumCached ) {
0060 
0061             if( m_deviceEntry->blockedId == 0 )
0062                 emit checkingMedium( m_deviceEntry->medium.device(), QString() );
0063 
0064             //
0065             // we block for writing before the update
0066             // This is important to make sure we do not overwrite a reset operation
0067             //
0068             m_deviceEntry->writeMutex.lock();
0069 
0070             //
0071             // The medium has changed. We need to update the information.
0072             //
0073             K3b::Medium m( m_deviceEntry->medium.device() );
0074             m.update();
0075 
0076             // block the info since it is not valid anymore
0077             m_deviceEntry->readMutex.lock();
0078 
0079             m_deviceEntry->medium = m;
0080 
0081             // the information is valid. let the info go.
0082             m_deviceEntry->readMutex.unlock();
0083             m_deviceEntry->writeMutex.unlock();
0084 
0085             //
0086             // inform the media cache about the media change
0087             //
0088             if( m_deviceEntry->blockedId == 0 )
0089                 emit mediumChanged( m_deviceEntry->medium.device() );
0090         }
0091 
0092         if( m_deviceEntry->blockedId == 0 )
0093             QThread::sleep( 2 );
0094     }
0095 }
0096 
0097 
0098 
0099 
0100 
0101 // ////////////////////////////////////////////////////////////////////////////////
0102 // MEDIA CACHE IMPL
0103 // ////////////////////////////////////////////////////////////////////////////////
0104 
0105 
0106 class K3b::MediaCache::Private
0107 {
0108 public:
0109     QMap<K3b::Device::Device*, DeviceEntry*> deviceMap;
0110     KCDDB::Client cddbClient;
0111 
0112     K3b::MediaCache* q;
0113 
0114     void _k_mediumChanged( K3b::Device::Device* );
0115     void _k_cddbJobFinished( KJob* job );
0116 };
0117 
0118 
0119 // called from the device thread which updated the medium
0120 void K3b::MediaCache::Private::_k_mediumChanged( K3b::Device::Device* dev )
0121 {
0122     if ( q->medium( dev ).content() & K3b::Medium::ContentAudio ) {
0123         K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( q->medium( dev ) );
0124         connect( job, SIGNAL(result(KJob*)),
0125                  q, SLOT(_k_cddbJobFinished(KJob*)) );
0126         emit q->checkingMedium( dev, i18n( "CDDB Lookup" ) );
0127     }
0128     else {
0129         emit q->mediumChanged( dev );
0130     }
0131 }
0132 
0133 
0134 // once the cddb job is finished the medium is really updated
0135 void K3b::MediaCache::Private::_k_cddbJobFinished( KJob* job )
0136 {
0137     K3b::CDDB::CDDBJob* cddbJob = dynamic_cast<K3b::CDDB::CDDBJob*>( job );
0138     K3b::Medium oldMedium = cddbJob->medium();
0139 
0140     // make sure the medium did not change during the job
0141     if ( oldMedium.sameMedium( q->medium( oldMedium.device() ) ) ) {
0142         if ( !job->error() ) {
0143             // update it
0144             deviceMap[oldMedium.device()]->medium.d->cddbInfo = cddbJob->cddbResult();
0145             emit q->mediumCddbChanged( oldMedium.device() );
0146         }
0147 
0148         emit q->mediumChanged( oldMedium.device() );
0149     }
0150 }
0151 
0152 
0153 
0154 K3b::MediaCache::MediaCache( QObject* parent )
0155     : QObject( parent ),
0156       d( new Private() )
0157 {
0158     d->q = this;
0159 }
0160 
0161 
0162 K3b::MediaCache::~MediaCache()
0163 {
0164     clearDeviceList();
0165     delete d;
0166 }
0167 
0168 
0169 int K3b::MediaCache::blockDevice( K3b::Device::Device* dev )
0170 {
0171     qDebug() << dev->blockDeviceName();
0172     DeviceEntry* e = findDeviceEntry( dev );
0173     if( e ) {
0174         if( e->blockedId )
0175             return -1;
0176         else {
0177             // block the information
0178             e->readMutex.lock();
0179 
0180             // create (hopefully) unique id
0181             e->blockedId = QRandomGenerator::global()->bounded(RAND_MAX);
0182 
0183             // let the info go
0184             e->readMutex.unlock();
0185 
0186             // wait for the thread to stop
0187             e->thread->wait();
0188 
0189             return e->blockedId;
0190         }
0191     }
0192     else
0193         return -1;
0194 }
0195 
0196 
0197 bool K3b::MediaCache::unblockDevice( K3b::Device::Device* dev, int id )
0198 {
0199     qDebug() << dev->blockDeviceName();
0200     DeviceEntry* e = findDeviceEntry( dev );
0201     if( e && e->blockedId && e->blockedId == id ) {
0202         e->blockedId = 0;
0203 
0204         e->medium = K3b::Medium( dev );
0205 
0206         // restart the poll thread
0207         e->thread->start();
0208 
0209         return true;
0210     }
0211     else
0212         return false;
0213 }
0214 
0215 
0216 bool K3b::MediaCache::isBlocked( K3b::Device::Device* dev )
0217 {
0218     if( DeviceEntry* e = findDeviceEntry( dev ) )
0219         return ( e->blockedId != 0 );
0220     else
0221         return false;
0222 }
0223 
0224 
0225 K3b::Medium K3b::MediaCache::medium( K3b::Device::Device* dev )
0226 {
0227     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0228         e->readMutex.lock();
0229         K3b::Medium m = e->medium;
0230         e->readMutex.unlock();
0231         return m;
0232     }
0233     else
0234         return K3b::Medium();
0235 }
0236 
0237 
0238 K3b::Device::DiskInfo K3b::MediaCache::diskInfo( K3b::Device::Device* dev )
0239 {
0240     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0241         e->readMutex.lock();
0242         K3b::Device::DiskInfo di = e->medium.diskInfo();
0243         e->readMutex.unlock();
0244         return di;
0245     }
0246     else
0247         return K3b::Device::DiskInfo();
0248 }
0249 
0250 
0251 K3b::Device::Toc K3b::MediaCache::toc( K3b::Device::Device* dev )
0252 {
0253     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0254         e->readMutex.lock();
0255         K3b::Device::Toc toc = e->medium.toc();
0256         e->readMutex.unlock();
0257         return toc;
0258     }
0259     else
0260         return K3b::Device::Toc();
0261 }
0262 
0263 
0264 K3b::Device::CdText K3b::MediaCache::cdText( K3b::Device::Device* dev )
0265 {
0266     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0267         e->readMutex.lock();
0268         K3b::Device::CdText cdt = e->medium.cdText();
0269         e->readMutex.unlock();
0270         return cdt;
0271     }
0272     else
0273         return K3b::Device::CdText();
0274 }
0275 
0276 
0277 QList<int> K3b::MediaCache::writingSpeeds( K3b::Device::Device* dev )
0278 {
0279     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0280         e->readMutex.lock();
0281         QList<int> ws = e->medium.writingSpeeds();
0282         e->readMutex.unlock();
0283         return ws;
0284     }
0285     else
0286         return QList<int>();
0287 }
0288 
0289 
0290 QString K3b::MediaCache::mediumString( K3b::Device::Device* device, bool useContent )
0291 {
0292     if( DeviceEntry* e = findDeviceEntry( device ) ) {
0293         return e->medium.shortString( useContent ? Medium::WithContents : Medium::NoStringFlags );
0294     }
0295     else
0296         return QString();
0297 }
0298 
0299 
0300 void K3b::MediaCache::clearDeviceList()
0301 {
0302     qDebug();
0303 
0304     // make all the threads stop
0305     for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
0306          it != d->deviceMap.end(); ++it ) {
0307         it.value()->blockedId = 1;
0308     }
0309 
0310     // and remove them
0311     for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
0312          it != d->deviceMap.end(); ++it ) {
0313         qDebug() << " waiting for info thread " << it.key()->blockDeviceName() << " to finish";
0314         it.value()->thread->wait();
0315         delete it.value();
0316     }
0317 
0318     d->deviceMap.clear();
0319 }
0320 
0321 
0322 void K3b::MediaCache::buildDeviceList( K3b::Device::DeviceManager* dm )
0323 {
0324     // remember blocked ids
0325     QMap<K3b::Device::Device*, int> blockedIds;
0326     for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
0327          it != d->deviceMap.end(); ++it )
0328         blockedIds.insert( it.key(), it.value()->blockedId );
0329 
0330     clearDeviceList();
0331 
0332     QList<K3b::Device::Device *> items(dm->allDevices());
0333     for( QList<K3b::Device::Device *>::const_iterator it = items.constBegin();
0334          it != items.constEnd(); ++it ) {
0335         d->deviceMap.insert( *it, new DeviceEntry( this, *it ) );
0336         QMap<K3b::Device::Device*, int>::const_iterator bi_it = blockedIds.constFind( *it );
0337         if( bi_it != blockedIds.constEnd() )
0338             d->deviceMap[*it]->blockedId = bi_it.value();
0339     }
0340 
0341     // start all the polling threads
0342     for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
0343          it != d->deviceMap.end(); ++it ) {
0344         if( !it.value()->blockedId )
0345             it.value()->thread->start();
0346     }
0347 }
0348 
0349 
0350 K3b::MediaCache::DeviceEntry* K3b::MediaCache::findDeviceEntry( K3b::Device::Device* dev )
0351 {
0352     QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.find( dev );
0353     if( it != d->deviceMap.end() )
0354         return it.value();
0355     else
0356         return 0;
0357 }
0358 
0359 
0360 void K3b::MediaCache::lookupCddb( K3b::Device::Device* dev )
0361 {
0362     K3b::Medium m = medium( dev );
0363     if ( m.content() & K3b::Medium::ContentAudio ) {
0364         K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( m );
0365         connect( job, SIGNAL(result(KJob*)),
0366                  this, SLOT(_k_cddbJobFinished(KJob*)) );
0367         emit checkingMedium( dev, i18n( "CDDB Lookup" ) );
0368     }
0369 }
0370 
0371 
0372 void K3b::MediaCache::resetDevice( K3b::Device::Device* dev )
0373 {
0374     if( DeviceEntry* e = findDeviceEntry( dev ) ) {
0375         qDebug() << "Resetting medium in" << dev->blockDeviceName();
0376         e->writeMutex.lock();
0377         e->readMutex.lock();
0378         e->medium.reset();
0379         e->readMutex.unlock();
0380         e->writeMutex.unlock();
0381         // no need to emit mediumChanged here. The poll thread will act on it soon
0382     }
0383 }
0384 
0385 #include "moc_k3bmediacache.cpp"
0386 #include "moc_k3bmediacache_p.cpp"