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 }