void
ScrobblerAdapter::updateNowPlaying( const Meta::TrackPtr &track )
{
    lastfm::MutableTrack lfmTrack;
    if( track )
    {
        if( isToBeSkipped( track ) )
        {
            debug() << "updateNowPlaying(): refusing track" << track->prettyUrl()
                    << "- contains label:" << m_config->filteredLabel() << "which is marked to be skipped";
            return;
        }
        copyTrackMetadata( lfmTrack, track );
        debug() << "nowPlaying: " << lfmTrack.artist() << "-" << lfmTrack.album() << "-"
                << lfmTrack.title() << "source:" << lfmTrack.source() << "duration:"
                << lfmTrack.duration();
        m_scrobbler.nowPlaying( lfmTrack );
    }
    else
    {
        debug() << "removeNowPlaying";
        QNetworkReply *reply = lfmTrack.removeNowPlaying(); // works even with empty lfmTrack
        connect( reply, SIGNAL(finished()), reply, SLOT(deleteLater()) ); // don't leak
    }
}
StatSyncing::ScrobblingService::ScrobbleError
ScrobblerAdapter::scrobble( const Meta::TrackPtr &track, double playedFraction,
                            const QDateTime &time )
{
    Q_ASSERT( track );
    if( isToBeSkipped( track ) )
    {
        debug() << "scrobble(): refusing track" << track->prettyUrl()
                << "- contains label:" << m_config->filteredLabel() << "which is marked to be skipped";
        return SkippedByUser;
    }
    if( track->length() * qMin( 1.0, playedFraction ) < 30 * 1000 )
    {
        debug() << "scrobble(): refusing track" << track->prettyUrl() << "- played time ("
                << track->length() / 1000 << "*" << playedFraction << "s) shorter than 30 s";
        return TooShort;
    }
    int playcount = qRound( playedFraction );
    if( playcount <= 0 )
    {
        debug() << "scrobble(): refusing track" << track->prettyUrl() << "- played "
                << "fraction (" << playedFraction * 100 << "%) less than 50 %";
        return TooShort;
    }

    lastfm::MutableTrack lfmTrack;
    copyTrackMetadata( lfmTrack, track );
    // since liblastfm >= 1.0.3 it interprets following extra property:
    lfmTrack.setExtra( "playCount", QString::number( playcount ) );
    lfmTrack.setTimeStamp( time.isValid() ? time : QDateTime::currentDateTime() );
    debug() << "scrobble: " << lfmTrack.artist() << "-" << lfmTrack.album() << "-"
            << lfmTrack.title() << "source:" << lfmTrack.source() << "duration:"
            << lfmTrack.duration();
    m_scrobbler.cache( lfmTrack );
    m_scrobbler.submit(); // since liblastfm 1.0.7, submit() is not called automatically upon cache()
    switch( lfmTrack.scrobbleStatus() )
    {
        case lastfm::Track::Cached:
        case lastfm::Track::Submitted:
            return NoError;
        case lastfm::Track::Null:
        case lastfm::Track::Error:
            break;
    }
    return BadMetadata;
}
void
OrganizeCollectionDialog::previewNextBatch() //private slot
{
    const int batchSize = 10;

    QMap<Meta::TrackPtr, QString> dests = m_trackOrganizer->getDestinations( batchSize );
    QMapIterator<Meta::TrackPtr, QString> it( dests );
    while( it.hasNext() )
    {
        it.next();
        Meta::TrackPtr track = it.key();

        QString originalPath = track->prettyUrl();
        QString newPath = it.value();

        int newRow = ui->previewTableWidget->rowCount();
        ui->previewTableWidget->insertRow( newRow );

        //new path preview in the 1st column
        QPalette p = ui->previewTableWidget->palette();
        QTableWidgetItem *item = new QTableWidgetItem( newPath );
        KColorScheme::adjustBackground( p, KColorScheme::NegativeBackground );
        if( QFileInfo( newPath ).exists() )
        {
            item->setBackgroundColor( p.color( QPalette::Base ) );
            m_conflict = true;
        }
        ui->previewTableWidget->setItem( newRow, 0, item );

        //original in the second column
        item = new QTableWidgetItem( originalPath );
        ui->previewTableWidget->setItem( newRow, 1, item );
    }

    if( m_conflict )
    {
        if( ui->overwriteCheck->isChecked() )
            ui->conflictLabel->setText( i18n( "There is a filename conflict, existing files will be overwritten." ) );
        else
            ui->conflictLabel->setText( i18n( "There is a filename conflict, existing files will not be changed." ) );
    }
    else
        ui->conflictLabel->setText(""); // we clear the text instead of hiding it to retain the layout spacing

    //non-blocking way of updating the preview table.
    if( dests.count() == batchSize )
        QTimer::singleShot( 10, this, SLOT(previewNextBatch()) );
    else
        unsetCursor(); // finished
}
Exemple #4
0
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();
    }
}