1 /*
2 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "k3bmediacache.h"
7 #include "k3bmediacache_p.h"
8 #include "k3bmedium.h"
9 #include "k3bmedium_p.h"
10 #include "k3bcddb.h"
11 #include "k3bdevicemanager.h"
12 #include "k3bdeviceglobals.h"
13 #include "k3bcore.h"
14 #include "k3b_i18n.h"
15
16 #include <QDebug>
17 #include <QThread>
18 #include <QMutex>
19 #include <QEvent>
20 #include <QRandomGenerator>
21
22 #include <KCddb/Client>
23
24
25
DeviceEntry(K3b::MediaCache * c,K3b::Device::Device * dev)26 K3b::MediaCache::DeviceEntry::DeviceEntry( K3b::MediaCache* c, K3b::Device::Device* dev )
27 : medium(dev),
28 blockedId(0),
29 cache(c)
30 {
31 thread = new K3b::MediaCache::PollThread( this );
32 connect( thread, SIGNAL(mediumChanged(K3b::Device::Device*)),
33 c, SLOT(_k_mediumChanged(K3b::Device::Device*)),
34 Qt::QueuedConnection );
35 connect( thread, SIGNAL(checkingMedium(K3b::Device::Device*,QString)),
36 c, SIGNAL(checkingMedium(K3b::Device::Device*,QString)),
37 Qt::QueuedConnection );
38 }
39
40
~DeviceEntry()41 K3b::MediaCache::DeviceEntry::~DeviceEntry()
42 {
43 delete thread;
44 }
45
46
run()47 void K3b::MediaCache::PollThread::run()
48 {
49 while( m_deviceEntry->blockedId == 0 ) {
50 bool unitReady = m_deviceEntry->medium.device()->testUnitReady();
51 bool mediumCached = ( m_deviceEntry->medium.diskInfo().diskState() != K3b::Device::STATE_NO_MEDIA );
52
53 //
54 // we only get the other information in case the disk state changed or if we have
55 // no info at all (FIXME: there are drives around that are not able to provide a proper
56 // disk state)
57 //
58 if( m_deviceEntry->medium.diskInfo().diskState() == K3b::Device::STATE_UNKNOWN ||
59 unitReady != mediumCached ) {
60
61 if( m_deviceEntry->blockedId == 0 )
62 emit checkingMedium( m_deviceEntry->medium.device(), QString() );
63
64 //
65 // we block for writing before the update
66 // This is important to make sure we do not overwrite a reset operation
67 //
68 m_deviceEntry->writeMutex.lock();
69
70 //
71 // The medium has changed. We need to update the information.
72 //
73 K3b::Medium m( m_deviceEntry->medium.device() );
74 m.update();
75
76 // block the info since it is not valid anymore
77 m_deviceEntry->readMutex.lock();
78
79 m_deviceEntry->medium = m;
80
81 // the information is valid. let the info go.
82 m_deviceEntry->readMutex.unlock();
83 m_deviceEntry->writeMutex.unlock();
84
85 //
86 // inform the media cache about the media change
87 //
88 if( m_deviceEntry->blockedId == 0 )
89 emit mediumChanged( m_deviceEntry->medium.device() );
90 }
91
92 if( m_deviceEntry->blockedId == 0 )
93 QThread::sleep( 2 );
94 }
95 }
96
97
98
99
100
101 // ////////////////////////////////////////////////////////////////////////////////
102 // MEDIA CACHE IMPL
103 // ////////////////////////////////////////////////////////////////////////////////
104
105
106 class K3b::MediaCache::Private
107 {
108 public:
109 QMap<K3b::Device::Device*, DeviceEntry*> deviceMap;
110 KCDDB::Client cddbClient;
111
112 K3b::MediaCache* q;
113
114 void _k_mediumChanged( K3b::Device::Device* );
115 void _k_cddbJobFinished( KJob* job );
116 };
117
118
119 // called from the device thread which updated the medium
_k_mediumChanged(K3b::Device::Device * dev)120 void K3b::MediaCache::Private::_k_mediumChanged( K3b::Device::Device* dev )
121 {
122 if ( q->medium( dev ).content() & K3b::Medium::ContentAudio ) {
123 K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( q->medium( dev ) );
124 connect( job, SIGNAL(result(KJob*)),
125 q, SLOT(_k_cddbJobFinished(KJob*)) );
126 emit q->checkingMedium( dev, i18n( "CDDB Lookup" ) );
127 }
128 else {
129 emit q->mediumChanged( dev );
130 }
131 }
132
133
134 // once the cddb job is finished the medium is really updated
_k_cddbJobFinished(KJob * job)135 void K3b::MediaCache::Private::_k_cddbJobFinished( KJob* job )
136 {
137 K3b::CDDB::CDDBJob* cddbJob = dynamic_cast<K3b::CDDB::CDDBJob*>( job );
138 K3b::Medium oldMedium = cddbJob->medium();
139
140 // make sure the medium did not change during the job
141 if ( oldMedium.sameMedium( q->medium( oldMedium.device() ) ) ) {
142 if ( !job->error() ) {
143 // update it
144 deviceMap[oldMedium.device()]->medium.d->cddbInfo = cddbJob->cddbResult();
145 emit q->mediumCddbChanged( oldMedium.device() );
146 }
147
148 emit q->mediumChanged( oldMedium.device() );
149 }
150 }
151
152
153
MediaCache(QObject * parent)154 K3b::MediaCache::MediaCache( QObject* parent )
155 : QObject( parent ),
156 d( new Private() )
157 {
158 d->q = this;
159 }
160
161
~MediaCache()162 K3b::MediaCache::~MediaCache()
163 {
164 clearDeviceList();
165 delete d;
166 }
167
168
blockDevice(K3b::Device::Device * dev)169 int K3b::MediaCache::blockDevice( K3b::Device::Device* dev )
170 {
171 qDebug() << dev->blockDeviceName();
172 DeviceEntry* e = findDeviceEntry( dev );
173 if( e ) {
174 if( e->blockedId )
175 return -1;
176 else {
177 // block the information
178 e->readMutex.lock();
179
180 // create (hopefully) unique id
181 e->blockedId = QRandomGenerator::global()->bounded(RAND_MAX);
182
183 // let the info go
184 e->readMutex.unlock();
185
186 // wait for the thread to stop
187 e->thread->wait();
188
189 return e->blockedId;
190 }
191 }
192 else
193 return -1;
194 }
195
196
unblockDevice(K3b::Device::Device * dev,int id)197 bool K3b::MediaCache::unblockDevice( K3b::Device::Device* dev, int id )
198 {
199 qDebug() << dev->blockDeviceName();
200 DeviceEntry* e = findDeviceEntry( dev );
201 if( e && e->blockedId && e->blockedId == id ) {
202 e->blockedId = 0;
203
204 e->medium = K3b::Medium( dev );
205
206 // restart the poll thread
207 e->thread->start();
208
209 return true;
210 }
211 else
212 return false;
213 }
214
215
isBlocked(K3b::Device::Device * dev)216 bool K3b::MediaCache::isBlocked( K3b::Device::Device* dev )
217 {
218 if( DeviceEntry* e = findDeviceEntry( dev ) )
219 return ( e->blockedId != 0 );
220 else
221 return false;
222 }
223
224
medium(K3b::Device::Device * dev)225 K3b::Medium K3b::MediaCache::medium( K3b::Device::Device* dev )
226 {
227 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
228 e->readMutex.lock();
229 K3b::Medium m = e->medium;
230 e->readMutex.unlock();
231 return m;
232 }
233 else
234 return K3b::Medium();
235 }
236
237
diskInfo(K3b::Device::Device * dev)238 K3b::Device::DiskInfo K3b::MediaCache::diskInfo( K3b::Device::Device* dev )
239 {
240 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
241 e->readMutex.lock();
242 K3b::Device::DiskInfo di = e->medium.diskInfo();
243 e->readMutex.unlock();
244 return di;
245 }
246 else
247 return K3b::Device::DiskInfo();
248 }
249
250
toc(K3b::Device::Device * dev)251 K3b::Device::Toc K3b::MediaCache::toc( K3b::Device::Device* dev )
252 {
253 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
254 e->readMutex.lock();
255 K3b::Device::Toc toc = e->medium.toc();
256 e->readMutex.unlock();
257 return toc;
258 }
259 else
260 return K3b::Device::Toc();
261 }
262
263
cdText(K3b::Device::Device * dev)264 K3b::Device::CdText K3b::MediaCache::cdText( K3b::Device::Device* dev )
265 {
266 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
267 e->readMutex.lock();
268 K3b::Device::CdText cdt = e->medium.cdText();
269 e->readMutex.unlock();
270 return cdt;
271 }
272 else
273 return K3b::Device::CdText();
274 }
275
276
writingSpeeds(K3b::Device::Device * dev)277 QList<int> K3b::MediaCache::writingSpeeds( K3b::Device::Device* dev )
278 {
279 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
280 e->readMutex.lock();
281 QList<int> ws = e->medium.writingSpeeds();
282 e->readMutex.unlock();
283 return ws;
284 }
285 else
286 return QList<int>();
287 }
288
289
mediumString(K3b::Device::Device * device,bool useContent)290 QString K3b::MediaCache::mediumString( K3b::Device::Device* device, bool useContent )
291 {
292 if( DeviceEntry* e = findDeviceEntry( device ) ) {
293 return e->medium.shortString( useContent ? Medium::WithContents : Medium::NoStringFlags );
294 }
295 else
296 return QString();
297 }
298
299
clearDeviceList()300 void K3b::MediaCache::clearDeviceList()
301 {
302 qDebug();
303
304 // make all the threads stop
305 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
306 it != d->deviceMap.end(); ++it ) {
307 it.value()->blockedId = 1;
308 }
309
310 // and remove them
311 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
312 it != d->deviceMap.end(); ++it ) {
313 qDebug() << " waiting for info thread " << it.key()->blockDeviceName() << " to finish";
314 it.value()->thread->wait();
315 delete it.value();
316 }
317
318 d->deviceMap.clear();
319 }
320
321
buildDeviceList(K3b::Device::DeviceManager * dm)322 void K3b::MediaCache::buildDeviceList( K3b::Device::DeviceManager* dm )
323 {
324 // remember blocked ids
325 QMap<K3b::Device::Device*, int> blockedIds;
326 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
327 it != d->deviceMap.end(); ++it )
328 blockedIds.insert( it.key(), it.value()->blockedId );
329
330 clearDeviceList();
331
332 QList<K3b::Device::Device *> items(dm->allDevices());
333 for( QList<K3b::Device::Device *>::const_iterator it = items.constBegin();
334 it != items.constEnd(); ++it ) {
335 d->deviceMap.insert( *it, new DeviceEntry( this, *it ) );
336 QMap<K3b::Device::Device*, int>::const_iterator bi_it = blockedIds.constFind( *it );
337 if( bi_it != blockedIds.constEnd() )
338 d->deviceMap[*it]->blockedId = bi_it.value();
339 }
340
341 // start all the polling threads
342 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin();
343 it != d->deviceMap.end(); ++it ) {
344 if( !it.value()->blockedId )
345 it.value()->thread->start();
346 }
347 }
348
349
findDeviceEntry(K3b::Device::Device * dev)350 K3b::MediaCache::DeviceEntry* K3b::MediaCache::findDeviceEntry( K3b::Device::Device* dev )
351 {
352 QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.find( dev );
353 if( it != d->deviceMap.end() )
354 return it.value();
355 else
356 return 0;
357 }
358
359
lookupCddb(K3b::Device::Device * dev)360 void K3b::MediaCache::lookupCddb( K3b::Device::Device* dev )
361 {
362 K3b::Medium m = medium( dev );
363 if ( m.content() & K3b::Medium::ContentAudio ) {
364 K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( m );
365 connect( job, SIGNAL(result(KJob*)),
366 this, SLOT(_k_cddbJobFinished(KJob*)) );
367 emit checkingMedium( dev, i18n( "CDDB Lookup" ) );
368 }
369 }
370
371
resetDevice(K3b::Device::Device * dev)372 void K3b::MediaCache::resetDevice( K3b::Device::Device* dev )
373 {
374 if( DeviceEntry* e = findDeviceEntry( dev ) ) {
375 qDebug() << "Resetting medium in" << dev->blockDeviceName();
376 e->writeMutex.lock();
377 e->readMutex.lock();
378 e->medium.reset();
379 e->readMutex.unlock();
380 e->writeMutex.unlock();
381 // no need to emit mediumChanged here. The poll thread will act on it soon
382 }
383 }
384
385 #include "moc_k3bmediacache.cpp"
386