File indexing completed on 2024-05-05 04:48:34
0001 /**************************************************************************************** 0002 * Copyright (c) 2010 Sergey Ivanov <123kash@gmail.com> * 0003 * Copyright (c) 2013 Alberto Villa <avilla@FreeBSD.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) any later * 0008 * version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along with * 0015 * this program. If not, see <http://www.gnu.org/licenses/>. * 0016 ****************************************************************************************/ 0017 0018 #define DEBUG_PREFIX "MusicBrainzTagsItem" 0019 0020 #include "MusicBrainzTagsItem.h" 0021 0022 #include "AmarokMimeData.h" 0023 #include "MusicBrainzMeta.h" 0024 #include "core/support/Debug.h" 0025 #include "core/collections/QueryMaker.h" 0026 0027 MusicBrainzTagsItem::MusicBrainzTagsItem( MusicBrainzTagsItem *parent, 0028 const Meta::TrackPtr &track, 0029 const QVariantMap &tags ) 0030 : m_parent( parent ) 0031 , m_track( track ) 0032 , m_data( tags ) 0033 , m_chosen( false ) 0034 , m_dataLock( QReadWriteLock::Recursive ) 0035 , m_parentLock( QReadWriteLock::Recursive ) 0036 , m_childrenLock( QReadWriteLock::Recursive ) 0037 { 0038 } 0039 0040 MusicBrainzTagsItem::~MusicBrainzTagsItem() 0041 { 0042 qDeleteAll( m_childItems ); 0043 } 0044 0045 MusicBrainzTagsItem * 0046 MusicBrainzTagsItem::parent() const 0047 { 0048 QReadLocker lock( &m_parentLock ); 0049 return m_parent; 0050 } 0051 0052 void 0053 MusicBrainzTagsItem::setParent( MusicBrainzTagsItem *parent ) 0054 { 0055 QWriteLocker lock( &m_parentLock ); 0056 m_parent = parent; 0057 } 0058 0059 MusicBrainzTagsItem * 0060 MusicBrainzTagsItem::child( const int row ) const 0061 { 0062 QReadLocker lock( &m_childrenLock ); 0063 return m_childItems.value( row ); 0064 } 0065 0066 void 0067 MusicBrainzTagsItem::appendChild( MusicBrainzTagsItem *newItem ) 0068 { 0069 QWriteLocker lock( &m_childrenLock ); 0070 m_childItems.append( newItem ); 0071 newItem->setParent( this ); 0072 0073 if( !newItem->data().isEmpty() ) 0074 { 0075 newItem->recalcSimilarityRate(); 0076 0077 #define MAKE_DATA_LIST( k ) { QVariantList list; if( newItem->dataContains( k ) ) list.append( newItem->dataValue( k ) ); newItem->dataInsert( k, list ); } 0078 0079 MAKE_DATA_LIST( MusicBrainz::TRACKID ); 0080 MAKE_DATA_LIST( MusicBrainz::ARTISTID ); 0081 MAKE_DATA_LIST( MusicBrainz::RELEASEID ); 0082 0083 #undef MAKE_DATA_LIST 0084 } 0085 } 0086 0087 void 0088 MusicBrainzTagsItem::mergeData( const QVariantMap &tags ) 0089 { 0090 if( tags.isEmpty() ) 0091 return; 0092 0093 MusicBrainzTagsItem fakeItem( this, m_track, tags ); 0094 // Calculate the future score of the result when merged. 0095 if( !fakeItem.dataContains( MusicBrainz::MUSICBRAINZ ) && dataContains( MusicBrainz::MUSICBRAINZ ) ) 0096 fakeItem.dataInsert( MusicBrainz::MUSICBRAINZ, dataValue( MusicBrainz::MUSICBRAINZ ) ); 0097 0098 if( !fakeItem.dataContains( MusicBrainz::MUSICDNS ) && dataContains( MusicBrainz::MUSICDNS ) ) 0099 fakeItem.dataInsert( MusicBrainz::MUSICDNS, dataValue( MusicBrainz::MUSICDNS ) ); 0100 0101 fakeItem.recalcSimilarityRate(); 0102 0103 QVariantList trackList = dataValue( MusicBrainz::TRACKID ).toList(); 0104 QVariantList artistList = dataValue( MusicBrainz::ARTISTID ).toList(); 0105 QVariantList releaseList = dataValue( MusicBrainz::RELEASEID ).toList(); 0106 if( fakeItem.score() > score() ) 0107 { 0108 // Update the score. 0109 if( fakeItem.dataContains( MusicBrainz::MUSICBRAINZ ) ) 0110 dataInsert( MusicBrainz::MUSICBRAINZ, fakeItem.dataValue( MusicBrainz::MUSICBRAINZ ) ); 0111 0112 if( fakeItem.dataContains( MusicBrainz::MUSICDNS ) ) 0113 dataInsert( MusicBrainz::MUSICDNS, fakeItem.dataValue( MusicBrainz::MUSICDNS ) ); 0114 0115 recalcSimilarityRate(); 0116 0117 if( fakeItem.dataContains( MusicBrainz::TRACKID ) ) 0118 trackList.prepend( fakeItem.dataValue( MusicBrainz::TRACKID ) ); 0119 0120 if( fakeItem.dataContains( MusicBrainz::ARTISTID ) ) 0121 artistList.prepend( fakeItem.dataValue( MusicBrainz::ARTISTID ) ); 0122 0123 if( fakeItem.dataContains( MusicBrainz::RELEASEID ) ) 0124 releaseList.prepend( fakeItem.dataValue( MusicBrainz::RELEASEID ) ); 0125 } 0126 else 0127 { 0128 if( fakeItem.dataContains( MusicBrainz::TRACKID ) ) 0129 trackList.append( fakeItem.dataValue( MusicBrainz::TRACKID ) ); 0130 0131 if( fakeItem.dataContains( MusicBrainz::ARTISTID ) ) 0132 artistList.append( fakeItem.dataValue( MusicBrainz::ARTISTID ) ); 0133 0134 if( fakeItem.dataContains( MusicBrainz::RELEASEID ) ) 0135 releaseList.append( fakeItem.dataValue( MusicBrainz::RELEASEID ) ); 0136 } 0137 0138 dataInsert( MusicBrainz::TRACKID, trackList ); 0139 dataInsert( MusicBrainz::ARTISTID, artistList ); 0140 dataInsert( MusicBrainz::RELEASEID, releaseList ); 0141 } 0142 0143 int 0144 MusicBrainzTagsItem::childCount() const 0145 { 0146 QReadLocker lock( &m_childrenLock ); 0147 return m_childItems.count(); 0148 } 0149 0150 int 0151 MusicBrainzTagsItem::row() const 0152 { 0153 if( parent() ) 0154 { 0155 QReadLocker lock( &m_childrenLock ); 0156 return m_parent->m_childItems.indexOf( const_cast<MusicBrainzTagsItem *>( this ) ); 0157 } 0158 0159 return 0; 0160 } 0161 0162 Meta::TrackPtr 0163 MusicBrainzTagsItem::track() const 0164 { 0165 QReadLocker lock( &m_dataLock ); 0166 return m_track; 0167 } 0168 0169 float 0170 MusicBrainzTagsItem::score() const 0171 { 0172 QReadLocker lock( &m_dataLock ); 0173 float score = dataValue( MusicBrainz::SIMILARITY ).toFloat(); 0174 0175 /* 0176 * Results of fingerprint-only lookup go on bottom as they are weak matches (only 0177 * their length is compared). 0178 */ 0179 if( !dataContains( MusicBrainz::MUSICBRAINZ ) ) 0180 score -= 1.0; 0181 0182 return score; 0183 } 0184 0185 QVariantMap 0186 MusicBrainzTagsItem::data() const 0187 { 0188 QReadLocker lock( &m_dataLock ); 0189 return m_data; 0190 } 0191 0192 QVariant 0193 MusicBrainzTagsItem::data( const int column ) const 0194 { 0195 if( m_data.isEmpty() ) 0196 { 0197 switch( column ) 0198 { 0199 case 0: 0200 { 0201 QString title; 0202 int trackNumber = m_track->trackNumber(); 0203 if( trackNumber > 0 ) 0204 title += QString( "%1 - " ).arg( trackNumber ); 0205 title += m_track->prettyName(); 0206 return title; 0207 } 0208 case 1: 0209 return ( !m_track->artist().isNull() )? m_track->artist()->name() : QVariant(); 0210 case 2: 0211 { 0212 if( m_track->album().isNull() ) 0213 return QVariant(); 0214 QString album = m_track->album()->name(); 0215 int discNumber = m_track->discNumber(); 0216 if( discNumber > 0 ) 0217 album += QString( " (disc %1)" ).arg( discNumber ); 0218 return album; 0219 } 0220 case 3: 0221 return ( !m_track->album().isNull() && m_track->album()->hasAlbumArtist() )? 0222 m_track->album()->albumArtist()->name() : QVariant(); 0223 case 4: 0224 return ( m_track->year()->year() > 0 )? m_track->year()->year() : QVariant(); 0225 } 0226 0227 return QVariant(); 0228 } 0229 0230 switch( column ) 0231 { 0232 case 0: 0233 { 0234 QString title; 0235 QVariant trackNumber = dataValue( Meta::Field::TRACKNUMBER ); 0236 if( trackNumber.toInt() > 0 ) 0237 { 0238 title += trackNumber.toString(); 0239 int trackCount = dataValue( MusicBrainz::TRACKCOUNT ).toInt(); 0240 if ( trackCount > 0 ) 0241 title += QString( "/%1" ).arg( trackCount ); 0242 title += " - "; 0243 } 0244 title += dataValue( Meta::Field::TITLE ).toString(); 0245 return title; 0246 } 0247 case 1: 0248 return dataValue( Meta::Field::ARTIST ); 0249 case 2: 0250 { 0251 QString album = dataValue( Meta::Field::ALBUM ).toString(); 0252 int discNumber = dataValue( Meta::Field::DISCNUMBER ).toInt(); 0253 if( discNumber > 0 ) 0254 album += QString( " (disc %1)" ).arg( discNumber ); 0255 return album; 0256 } 0257 case 3: 0258 return dataValue( Meta::Field::ALBUMARTIST ); 0259 case 4: 0260 return dataValue( Meta::Field::YEAR ); 0261 } 0262 0263 return QVariant(); 0264 } 0265 0266 void 0267 MusicBrainzTagsItem::setData( const QVariantMap &tags ) 0268 { 0269 QWriteLocker lock( &m_dataLock ); 0270 m_data = tags; 0271 } 0272 0273 bool 0274 MusicBrainzTagsItem::dataContains( const QString &key ) const 0275 { 0276 QReadLocker lock( &m_dataLock ); 0277 return m_data.contains( key ); 0278 } 0279 0280 QVariant 0281 MusicBrainzTagsItem::dataValue( const QString &key ) const 0282 { 0283 QReadLocker lock( &m_dataLock ); 0284 if( m_data.contains( key ) ) 0285 return m_data.value( key ); 0286 0287 return QVariant(); 0288 } 0289 0290 void 0291 MusicBrainzTagsItem::dataInsert( const QString &key, const QVariant &value ) 0292 { 0293 QWriteLocker lock( &m_dataLock ); 0294 m_data.insert( key, value ); 0295 } 0296 0297 bool 0298 MusicBrainzTagsItem::isChosen() const 0299 { 0300 QReadLocker lock( &m_dataLock ); 0301 if( m_data.isEmpty() ) 0302 { 0303 foreach( MusicBrainzTagsItem *item, m_childItems ) 0304 if( item->isChosen() ) 0305 return true; 0306 return false; 0307 } 0308 0309 return m_chosen; 0310 } 0311 0312 void 0313 MusicBrainzTagsItem::setChosen( bool chosen ) 0314 { 0315 if( m_data.isEmpty() ) 0316 return; 0317 0318 QWriteLocker lock( &m_dataLock ); 0319 m_chosen = chosen; 0320 } 0321 0322 MusicBrainzTagsItem * 0323 MusicBrainzTagsItem::chosenItem() const 0324 { 0325 if( m_data.isEmpty() ) 0326 { 0327 QReadLocker lock( &m_childrenLock ); 0328 foreach( MusicBrainzTagsItem *item, m_childItems ) 0329 if( item->isChosen() ) 0330 return item; 0331 } 0332 0333 return nullptr; 0334 } 0335 0336 bool 0337 MusicBrainzTagsItem::chooseBestMatch() 0338 { 0339 if( !m_data.isEmpty() || isChosen() ) 0340 return false; 0341 0342 QReadLocker lock( &m_childrenLock ); 0343 MusicBrainzTagsItem *bestMatch = nullptr; 0344 float maxScore = 0; 0345 foreach( MusicBrainzTagsItem *item, m_childItems ) 0346 { 0347 if( item->score() > maxScore ) 0348 { 0349 bestMatch = item; 0350 maxScore = item->score(); 0351 } 0352 } 0353 if( !bestMatch ) 0354 return false; 0355 0356 bestMatch->setChosen( true ); 0357 return true; 0358 } 0359 0360 bool 0361 MusicBrainzTagsItem::chooseBestMatchFromRelease( const QStringList &releases ) 0362 { 0363 if( !m_data.isEmpty() ) 0364 return false; 0365 0366 QReadLocker lock( &m_childrenLock ); 0367 if( !childCount() || isChosen() ) 0368 return false; 0369 0370 MusicBrainzTagsItem *bestMatch = nullptr; 0371 float maxScore = 0; 0372 QSet<QString> idList(releases.begin(), releases.end()); 0373 foreach( MusicBrainzTagsItem *item, m_childItems ) 0374 { 0375 /* 0376 * Match any of the releases referenced by selected entry. This should guarantee 0377 * that best results are always chosen when available. 0378 */ 0379 QStringList list = item->dataValue( MusicBrainz::RELEASEID ).toStringList(); 0380 QSet<QString> set(list.begin(), list.end()); 0381 if( item->score() > maxScore && 0382 !set.intersect( idList ).isEmpty() ) 0383 { 0384 bestMatch = item; 0385 maxScore = item->score(); 0386 } 0387 } 0388 0389 if( bestMatch ) 0390 { 0391 bestMatch->setChosen( true ); 0392 return true; 0393 } 0394 0395 return false; 0396 } 0397 0398 void 0399 MusicBrainzTagsItem::clearChoices() 0400 { 0401 QReadLocker lock( &m_childrenLock ); 0402 if( !parent() ) 0403 foreach( MusicBrainzTagsItem *item, m_childItems ) 0404 item->clearChoices(); 0405 else if( m_data.isEmpty() ) 0406 foreach( MusicBrainzTagsItem *item, m_childItems ) 0407 item->setChosen( false ); 0408 } 0409 0410 bool 0411 MusicBrainzTagsItem::isSimilar( const QVariantMap &tags ) const 0412 { 0413 QReadLocker lock( &m_dataLock ); 0414 QVariant empty; 0415 #define MATCH( k, t ) ( dataValue( k ).t() == ( tags.contains( k ) ? tags.value( k ) : empty ).t() ) 0416 /* 0417 * This is the information shown to the user: he will never be able to 0418 * distinguish between two tracks with the same information. 0419 */ 0420 return MATCH( Meta::Field::TITLE, toString ) && 0421 MATCH( Meta::Field::ARTIST, toString ) && 0422 MATCH( Meta::Field::ALBUM, toString ) && 0423 MATCH( Meta::Field::ALBUMARTIST, toString ) && 0424 MATCH( Meta::Field::YEAR, toInt ) && 0425 MATCH( MusicBrainz::TRACKCOUNT, toInt ) && 0426 MATCH( Meta::Field::DISCNUMBER, toInt ) && 0427 MATCH( Meta::Field::TRACKNUMBER, toInt ); 0428 #undef MATCH 0429 } 0430 0431 bool 0432 MusicBrainzTagsItem::operator==( const MusicBrainzTagsItem* item ) const 0433 { 0434 return isSimilar( item->data() ); 0435 } 0436 0437 bool 0438 MusicBrainzTagsItem::operator==( const Meta::TrackPtr &track) const 0439 { 0440 return m_track == track; 0441 } 0442 0443 void 0444 MusicBrainzTagsItem::recalcSimilarityRate() 0445 { 0446 dataInsert( MusicBrainz::SIMILARITY, dataValue( MusicBrainz::MUSICBRAINZ ).toFloat() + dataValue( MusicBrainz::MUSICDNS ).toFloat() ); 0447 }