void ScrobblerAdapter::copyTrackMetadata( lastfm::MutableTrack &to, const Meta::TrackPtr &track ) { to.setTitle( track->name() ); QString artistOrComposer; Meta::ComposerPtr composer = track->composer(); if( m_config->scrobbleComposer() && composer ) artistOrComposer = composer->name(); Meta::ArtistPtr artist = track->artist(); if( artistOrComposer.isEmpty() && artist ) artistOrComposer = artist->name(); to.setArtist( artistOrComposer ); Meta::AlbumPtr album = track->album(); Meta::ArtistPtr albumArtist; if( album ) { to.setAlbum( album->name() ); albumArtist = album->hasAlbumArtist() ? album->albumArtist() : Meta::ArtistPtr(); } if( albumArtist ) to.setAlbumArtist( albumArtist->name() ); to.setDuration( track->length() / 1000 ); if( track->trackNumber() >= 0 ) to.setTrackNumber( track->trackNumber() ); lastfm::Track::Source source = lastfm::Track::Player; if( track->type() == "stream/lastfm" ) source = lastfm::Track::LastFmRadio; else if( track->type().startsWith( "stream" ) ) source = lastfm::Track::NonPersonalisedBroadcast; else if( track->collection() && track->collection()->collectionId() != "localCollection" ) source = lastfm::Track::MediaDevice; to.setSource( source ); }
QString TrackOrganizer::buildDestination(const QString& format, const Meta::TrackPtr& track) const { // get hold of the shared pointers Meta::AlbumPtr album = track->album(); Meta::ArtistPtr artist = track->artist(); Meta::ComposerPtr composer = track->composer(); Meta::ArtistPtr albumArtist = album ? album->albumArtist() : Meta::ArtistPtr(); Meta::GenrePtr genre = track->genre(); Meta::YearPtr year = track->year(); bool isCompilation = album && album->isCompilation(); QMap<QString, QString> args; QString strArtist = artist ? artist->name() : QString(); QString strAlbumArtist = isCompilation ? i18n( "Various Artists" ) : ( albumArtist ? albumArtist->name() : strArtist ); args["theartist"] = strArtist; args["thealbumartist"] = strAlbumArtist; if( m_postfixThe ) { Amarok::manipulateThe( strArtist, true ); Amarok::manipulateThe( strAlbumArtist, true ); } if ( track->trackNumber() ) { QString trackNum = QString("%1").arg( track->trackNumber(), 2, 10, QChar('0') ); args["track"] = trackNum; } args["title"] = track->name(); args["artist"] = strArtist; args["composer"] = composer ? composer->name() : QString(); // if year == 0 then we don't want include it QString strYear = year ? year->name() : QString(); args["year"] = strYear.localeAwareCompare( "0" ) == 0 ? QString() : strYear; args["album"] = track->album() ? track->album()->name() : QString(); args["albumartist"] = strAlbumArtist; args["comment"] = track->comment(); args["genre"] = genre ? genre->name() : QString(); if( m_targetFileExtension.isEmpty() ) args["filetype"] = track->type(); else args["filetype"] = m_targetFileExtension; QString strFolder = QFileInfo( track->playableUrl().toLocalFile() ).path(); strFolder = strFolder.mid( commonPrefixLength( m_folderPrefix, strFolder ) ); args["folder"] = strFolder; args["initial"] = strAlbumArtist.mid( 0, 1 ).toUpper(); //artists starting with The are already handled above if( track->discNumber() ) args["discnumber"] = QString::number( track->discNumber() ); args["collectionroot"] = m_folderPrefix; // some additional properties not supported by organize dialog. args["rating"] = track->statistics()->rating(); args["filesize"] = track->filesize(); args["length"] = track->length() / 1000; // Fill up default empty values for StringX formater // TODO make this values changeable by user args["default_album"] = i18n( "Unknown album" ); args["default_albumartist"] = i18n( "Unknown artist" ); args["default_artist"] = args["albumartist"]; args["default_thealbumartist"] = args["albumartist"]; args["default_theartist"] = args["albumartist"]; args["default_comment"] = i18n( "No comments" ); args["default_composer"] = i18n( "Unknown composer" ); args["default_discnumber"] = i18n( "Unknown disc number" ); args["default_genre"] = i18n( "Unknown genre" ); args["default_title"] = i18n( "Unknown title" ); args["default_year"] = i18n( "Unknown year" ); foreach( const QString &key, args.keys() ) if( key != "collectionroot" && key != "folder" ) args[key] = args[key].replace( '/', '-' ); Amarok::QStringx formatx( format ); QString result = formatx.namedOptArgs( args ); return cleanPath( result ); }
void TestMetaConstants::testValueForField() { Meta::TrackPtr trackPtr; // When track is null, an invalid QVariant is returned trackPtr = 0; QVERIFY( !valueForField( 0, trackPtr ).isValid() ); // Set up mock track details and create mock track QVariantMap trackData; trackData[ Meta::Field::URL ] = KUrl( "file:///test/url" ); trackData[ Meta::Field::TITLE ] = "test track"; trackData[ Meta::Field::COMMENT ] = "test comment" ; trackData[ Meta::Field::TRACKNUMBER ] = 1; trackData[ Meta::Field::DISCNUMBER ] = 1; trackData[ Meta::Field::BPM ] = qreal( 1 ); trackData[ Meta::Field::LENGTH ] = qint64( 1 ); trackData[ Meta::Field::BITRATE ] = 1; trackData[ Meta::Field::SAMPLERATE ] = 1; trackData[ Meta::Field::FILESIZE ] = 1; trackData[ Meta::Field::SCORE ] = double( 1 ); trackData[ Meta::Field::RATING ] = 1; trackData[ Meta::Field::FIRST_PLAYED ] = QDateTime( QDate( 2012, 1, 1) ); trackData[ Meta::Field::LAST_PLAYED ] = QDateTime( QDate( 2012, 1, 1) ); trackData[ Meta::Field::PLAYCOUNT ] = 1; trackData[ Meta::Field::UNIQUEID ] = "test uid"; MetaMock *testTrack = new MetaMock( trackData ); // Associate track with album, artist, etc. MockAlbum *testAlbum = new MockAlbum( "test album" ); MockArtist *testArtist = new MockArtist( "test artist" ); MockComposer *testComposer = new MockComposer( "test composer" ); MockGenre *testGenre = new MockGenre( "test genre" ); MockYear *testYear = new MockYear( "2012" ); MockLabel *testLabel1 = new MockLabel( "test label 1"); MockLabel *testLabel2 = new MockLabel( "test label 2" ); MockLabel *testLabel3 = new MockLabel( "test label 3" ); // For valAlbumArtist testAlbum->m_albumArtist = testArtist; testTrack->m_album = testAlbum; testTrack->m_artist = testArtist; testTrack->m_composer = testComposer; testTrack->m_genre = testGenre; testTrack->m_year = testYear; testTrack->m_labels << Meta::LabelPtr( testLabel1 ) << Meta::LabelPtr( testLabel2 ) << Meta::LabelPtr( testLabel3 ); // Make the track pointer point to the created track trackPtr = Meta::TrackPtr( testTrack ); // Case 0 QVariant trackValue = valueForField( qint64( 0 ), trackPtr ); QVERIFY( trackValue.toStringList().contains( trackData.value( Meta::Field::URL ).value<KUrl>().path() + trackData.value( Meta::Field::TITLE ).toString() + trackData.value( Meta::Field::COMMENT ).toString() ) ); QVERIFY( trackValue.toStringList().contains( testAlbum->name() ) ); QVERIFY( trackValue.toStringList().contains( testArtist->name() ) ); QVERIFY( trackValue.toStringList().contains( testGenre->name() ) ); // Case Meta::valUrl trackValue = valueForField( Meta::valUrl, trackPtr ); QVERIFY( trackValue.toString() == trackData.value( Meta::Field::URL ).value<KUrl>().path() ); // Case Meta::valTitle trackValue = valueForField( Meta::valTitle, trackPtr ); QVERIFY( trackValue.toString() == trackData.value( Meta::Field::TITLE ).toString() ); // Case Meta::valArtist for non-null artist trackValue = valueForField( Meta::valArtist, trackPtr ); QVERIFY( trackValue.toString() == testArtist->name() ); // Case Meta::valAlbum for non-null album trackValue = valueForField( Meta::valAlbum, trackPtr ); QVERIFY( trackValue.toString() == testAlbum->name() ); // Case Meta::valComposer for non-null composer trackValue = valueForField( Meta::valComposer, trackPtr ); QVERIFY( trackValue.toString() == testComposer->name() ); // Case Meta::valGenre for non-null genre trackValue = valueForField( Meta::valGenre, trackPtr ); QVERIFY( trackValue.toString() == testGenre->name() ); // Case Meta::valYear for non-null year trackValue = valueForField( Meta::valYear, trackPtr ); QVERIFY( trackValue.toInt() == testYear->name().toInt() ); // Case Meta::valComment trackValue = valueForField( Meta::valComment, trackPtr ); QVERIFY( trackValue.toString() == trackData.value( Meta::Field::COMMENT ).toString() ); // Case Meta::valTrackNr trackValue = valueForField( Meta::valTrackNr, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::TRACKNUMBER ).toInt() ); // Case Meta::valDiscNr trackValue = valueForField( Meta::valDiscNr, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::DISCNUMBER ).toInt() ); // Case Meta::valBpm trackValue = valueForField( Meta::valBpm, trackPtr ); QVERIFY( trackValue.toDouble() == trackData.value( Meta::Field::BPM ).toDouble() ); // Case Meta::valLength trackValue = valueForField( Meta::valLength, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::LENGTH ).toInt()); // Case Meta::valBitrate trackValue = valueForField( Meta::valBitrate, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::BITRATE ).toInt() ); // Case Meta::valSamplerate trackValue = valueForField( Meta::valSamplerate, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::SAMPLERATE ).toInt() ); // Case Meta::valFilesize trackValue = valueForField( Meta::valFilesize, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::FILESIZE ).toInt() ); // Case Meta::valFormat trackValue = valueForField( Meta::valFormat, trackPtr ); QVERIFY( trackValue.toInt() == int( Amarok::FileTypeSupport::fileType(trackPtr->type() ) ) ); // Case Meta::valCreateDate trackValue = valueForField( Meta::valCreateDate, trackPtr ); QVERIFY( trackValue.toDateTime().isNull() ); // Case Meta::valScore trackValue = valueForField( Meta::valScore, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::SCORE ).toInt() ); // Case Meta::valRating trackValue = valueForField( Meta::valRating, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::RATING ).toInt() ); // Case Meta::valFirstPlayed trackValue = valueForField( Meta::valFirstPlayed, trackPtr ); QVERIFY( trackValue.toDateTime() == trackData.value( Meta::Field::FIRST_PLAYED ).toDateTime() ); // Case Meta::valLastPlayed trackValue = valueForField( Meta::valLastPlayed, trackPtr ); QVERIFY( trackValue.toDateTime() == trackData.value( Meta::Field::LAST_PLAYED ).toDateTime() ); // Case Meta::valFilesize trackValue = valueForField( Meta::valPlaycount, trackPtr ); QVERIFY( trackValue.toInt() == trackData.value( Meta::Field::PLAYCOUNT ).toInt() ); // Case Meta::valUniqueId trackValue = valueForField( Meta::valUniqueId, trackPtr ); QVERIFY( trackValue.toString() == trackData.value( Meta::Field::UNIQUEID ).toString() ); // Case Meta::valTrackGain trackValue = valueForField( Meta::valTrackGain, trackPtr ); QVERIFY( trackValue.toString() == "track gain" ); // Case Meta::valTrackGainPeak trackValue = valueForField( Meta::valTrackGainPeak, trackPtr ); QVERIFY( trackValue.toString() == "track gain peak" ); // Case Meta::valAlbumGainGain trackValue = valueForField( Meta::valAlbumGain, trackPtr ); QVERIFY( trackValue.toString() == "album gain" ); // Case Meta::valAlbumGain trackValue = valueForField( Meta::valAlbumGainPeak, trackPtr ); QVERIFY( trackValue.toString() == "album gain peak" ); // Case Meta::valAlbumArtist trackValue = valueForField( Meta::valAlbumArtist, trackPtr ); QVERIFY( trackValue.toString() == testAlbum->albumArtist()->name() ); // Case Meta::valLabel, order of the label names remains same trackValue = valueForField( Meta::valLabel, trackPtr ); QStringList labelNames; labelNames << testLabel1->name() << testLabel2->name() << testLabel3->name(); QVERIFY( trackValue.toStringList() == labelNames ); // Case Meta::valModified trackValue = valueForField( Meta::valModified, trackPtr ); QVERIFY( trackValue.toDateTime().isNull() ); // Default, returns an invalid QVariant trackValue = valueForField( qint64( -1 ), trackPtr ); QVERIFY( !trackValue.isValid() ); // Cases with null artist, album, etc. where an invalid QVariant is returned testAlbum->m_albumArtist = 0; testTrack->m_album = 0; testTrack->m_artist = 0; testTrack->m_composer = 0; testTrack->m_genre = 0; testTrack->m_year = 0; // Case Meta::valArtist for null artist trackValue = valueForField( Meta::valArtist, trackPtr ); QVERIFY( !trackValue.isValid() ); // Case Meta::valAlbum for null album trackValue = valueForField( Meta::valAlbum, trackPtr ); QVERIFY( !trackValue.isValid() ); // Case Meta::valComposer for null composer trackValue = valueForField( Meta::valComposer, trackPtr ); QVERIFY( !trackValue.isValid() ); // Case Meta::valGenre for null genre trackValue = valueForField( Meta::valGenre, trackPtr ); QVERIFY( !trackValue.isValid() ); // Case Meta::valYear for null year trackValue = valueForField( Meta::valYear, trackPtr ); QVERIFY( !trackValue.isValid() ); // Case Meta::valAlbumArtist for null album artist trackValue = valueForField( Meta::valAlbumArtist, trackPtr ); QVERIFY( !trackValue.isValid() ); }
void IpodCopyTracksJob::run() { if( !m_coll ) return; // destructed behind our back float totalSafeCapacity = m_coll.data()->totalCapacity() - m_coll.data()->capacityMargin(); QByteArray mountPoint = QFile::encodeName( m_coll.data()->mountPoint() ); QString collectionPrettyName = m_coll.data()->prettyName(); int trackCount = m_sources.size(); QString operationText; if( m_transcodingConfig.isJustCopy() ) operationText = i18np( "Transferring one track to %2", "Transferring %1 tracks to %2", trackCount, collectionPrettyName ); else operationText = i18np( "Transcoding one track to %2", "Transcoding %1 tracks to %2", trackCount, collectionPrettyName ); Amarok::Components::logger()->newProgressOperation( this, operationText, trackCount, this, SLOT(abort()) ); itdb_start_sync( m_coll.data()->m_itdb ); QMapIterator<Meta::TrackPtr, KUrl> it( m_sources ); while( it.hasNext() ) { if( m_aborted || !m_coll ) break; it.next(); Meta::TrackPtr track = it.key(); KUrl sourceUrl = it.value(); emit startDuplicateTrackSearch( track ); // wait for searching to finish: m_searchingForDuplicates.acquire( 1 ); if( m_duplicateTrack ) { trackProcessed( Duplicate, track, m_duplicateTrack ); continue; } if( !m_coll ) break; // destructed behind our back if( m_transcodingConfig.isJustCopy() // if not copying, we catch big files later && track->filesize() > totalSafeCapacity - m_coll.data()->usedCapacity() ) { // this is a best effort check, we do one definite one after the file is copied debug() << "Refusing to copy" << track->prettyUrl() << "to iPod: there are only" << totalSafeCapacity - m_coll.data()->usedCapacity() << "free bytes (not" << "counting a safety margin) on iPod and track has" << track->filesize() << "bytes."; trackProcessed( ExceededingSafeCapacity, track ); continue; } QString fileExtension; if( m_transcodingConfig.isJustCopy() ) fileExtension = track->type(); else fileExtension = Amarok::Components::transcodingController()->format( m_transcodingConfig.encoder() )->fileExtension(); if( !m_coll.data()->supportedFormats().contains( fileExtension ) ) { m_notPlayableFormats.insert( fileExtension ); trackProcessed( NotPlayable, track ); continue; } QByteArray fakeSrcName( "filename." ); // only for file extension fakeSrcName.append( QFile::encodeName( fileExtension ) ); /* determine destination filename; we cannot use ipodTrack because as it has no itdb * (and thus mountpoint) set */ GError *error = 0; gchar *destFilename = itdb_cp_get_dest_filename( 0, mountPoint, fakeSrcName, &error ); if( error ) { warning() << "Cannot construct iPod track filename:" << error->message; g_error_free( error ); error = 0; } if( !destFilename ) { trackProcessed( InternalError, track ); continue; } // start the physical copying KUrl destUrl = KUrl( QFile::decodeName( destFilename ) ); emit startCopyOrTranscodeJob( sourceUrl, destUrl ); // wait for copying to finish: m_copying.acquire( 1 ); /* fsync so that progress bar gives correct info and user doesnt remove the iPod * prematurely */ QFile destFile( QFile::decodeName( destFilename ) ); if( !destFile.exists() ) { debug() << destFile.fileName() << "does not exist even though we thought we copied it to iPod"; trackProcessed( CopyingFailed, track ); continue; } if( !m_coll ) break; // destructed behind our back if( m_coll.data()->usedCapacity() > totalSafeCapacity ) { debug() << "We exceeded total safe-to-use capacity on iPod (safe-to-use:" << totalSafeCapacity << "B, used:" << m_coll.data()->usedCapacity() << "): removing copied track from iPod"; destFile.remove(); trackProcessed( ExceededingSafeCapacity, track ); continue; } // fsync needs a file opened for writing, and without Apped it truncates files (?) if( !destFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) { warning() << "Cannot open file copied to ipod (for writing):" << destFile.fileName() << ": removing it"; destFile.remove(); trackProcessed( InternalError, track ); continue; } if( destFile.size() ) fsync( destFile.handle() ); // should flush all kernel buffers to disk destFile.close(); // create a new track object by copying meta-data from existing one: IpodMeta::Track *ipodTrack = new IpodMeta::Track( track ); // tell the track it has been copied: bool accepted = ipodTrack->finalizeCopying( mountPoint, destFilename ); g_free( destFilename ); destFilename = 0; if( !accepted ) { debug() << "ipodTrack->finalizeCopying( destFilename ) returned false!"; delete ipodTrack; trackProcessed( InternalError, track ); continue; } if( !m_transcodingConfig.isJustCopy() ) { // we need to reread some metadata in case the file was transcoded Meta::FieldHash fields = Meta::Tag::readTags( destFile.fileName() ); ipodTrack->setBitrate( fields.value( Meta::valBitrate, 0 ).toInt() ); ipodTrack->setLength( fields.value( Meta::valLength, 0 ).toLongLong() ); ipodTrack->setSampleRate( fields.value( Meta::valSamplerate, 0 ).toInt() ); Amarok::FileType type = Amarok::FileType( fields.value( Meta::valFormat, 0 ).toInt() ); ipodTrack->setType( Amarok::FileTypeSupport::toString( type ) ); // we retain ReplayGain, tags etc - these shouldn't change; size is read // in finalizeCopying() } // add the track to collection if( !m_coll ) { delete ipodTrack; break; // we were waiting for copying, m_coll may got destoryed } Meta::TrackPtr newTrack = m_coll.data()->addTrack( ipodTrack ); if( !newTrack ) { destFile.remove(); trackProcessed( InternalError, track ); continue; } trackProcessed( Success, track, newTrack ); } if( m_coll ) itdb_stop_sync( m_coll.data()->m_itdb ); emit endProgressOperation( this ); int sourceSize = m_sources.size(); int successCount = m_sourceTrackStatus.count( Success ); int duplicateCount = m_sourceTrackStatus.count( Duplicate ); QString transferredText; if ( m_transcodingConfig.isJustCopy() ) transferredText = i18ncp( "%2 is collection name", "Transferred one track to %2.", "Transferred %1 tracks to %2.", successCount, collectionPrettyName ); else transferredText = i18ncp( "%2 is collection name", "Transcoded one track to %2.", "Transcoded %1 tracks to %2.", successCount, collectionPrettyName ); if( successCount == sourceSize ) { Amarok::Components::logger()->shortMessage( transferredText ); } else if( m_aborted ) { QString text = i18np( "Transfer aborted. Managed to transfer one track.", "Transfer aborted. Managed to transfer %1 tracks.", successCount ); Amarok::Components::logger()->longMessage( text ); } else if( successCount + duplicateCount == sourceSize ) { QString text = i18ncp( "%2 is the 'Transferred 123 tracks to Some collection.' message", "%2 One track was already there.", "%2 %1 tracks were already there.", duplicateCount, transferredText ); Amarok::Components::logger()->longMessage( text ); } else { // somethig more severe failed, notify user using a dialog emit displaySorryDialog(); } }
QVariant Meta::valueForField( qint64 field, Meta::TrackPtr track ) { if( !track ) return QVariant(); switch( field ) { case 0: { // that is the simple search for MetaQueryWidget QSet<QString> allInfos; allInfos += track->playableUrl().path() += track->name() += track->comment(); if( track->artist() ) allInfos += track->artist()->name(); if( track->album() ) allInfos += track->album()->name(); if( track->genre() ) allInfos += track->genre()->name(); return QVariant( allInfos.toList() ); } case Meta::valUrl: return track->playableUrl().path(); case Meta::valTitle: return track->name(); case Meta::valArtist: return track->artist() ? QVariant(track->artist()->name()) : QVariant(); case Meta::valAlbum: return track->album() ? QVariant(track->album()->name()) : QVariant(); case Meta::valGenre: return track->genre() ? QVariant(track->genre()->name()) : QVariant(); case Meta::valComposer: return track->composer() ? QVariant(track->composer()->name()) : QVariant(); case Meta::valYear: return track->year() ? QVariant(track->year()->name().toInt()) : QVariant(); case Meta::valComment: return track->comment(); case Meta::valTrackNr: return track->trackNumber(); case Meta::valDiscNr: return track->discNumber(); case Meta::valBpm: return track->bpm(); case Meta::valLength: return track->length(); case Meta::valBitrate: return track->bitrate(); case Meta::valSamplerate: return track->sampleRate(); case Meta::valFilesize: return track->filesize(); case Meta::valFormat: return int(Amarok::FileTypeSupport::fileType(track->type())); case Meta::valCreateDate: return track->createDate(); case Meta::valScore: return track->statistics()->score(); case Meta::valRating: return track->statistics()->rating(); case Meta::valFirstPlayed: return track->statistics()->firstPlayed(); case Meta::valLastPlayed: return track->statistics()->lastPlayed(); case Meta::valPlaycount: return track->statistics()->playCount(); case Meta::valUniqueId: return track->uidUrl(); // todo case Meta::valTrackGain: return "track gain"; case Meta::valTrackGainPeak: return "track gain peak"; case Meta::valAlbumGain: return "album gain"; case Meta::valAlbumGainPeak: return "album gain peak"; case Meta::valAlbumArtist: return (track->album() && track->album()->albumArtist()) ? QVariant(track->album()->albumArtist()->name()) : QVariant(); case Meta::valLabel: { Meta::LabelList labels = track->labels(); QStringList strLabels; foreach( Meta::LabelPtr label, labels ) strLabels.append( label->name() ); return QVariant( strLabels ); } case Meta::valModified: return track->modifyDate(); default: return QVariant(); } }