Bug 218152

Summary: Amarok shall not copy files in parallel
Product: [Applications] amarok Reporter: Hakan Bayindir <hakan>
Component: Collections/LocalAssignee: Amarok Developers <amarok-bugs-dist>
Status: RESOLVED FIXED    
Severity: normal CC: kdebugs, kristjan.ugrin, matej, phull, ralf-engels, unnamedrambler
Priority: NOR    
Version: 2.2.2   
Target Milestone: 2.3.1   
Platform: unspecified   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: Output of "ps aux |grep amarok" on my computer during a big copy to collection operation of several albums

Description Hakan Bayindir 2009-12-10 19:54:01 UTC
Version:           2.2.1 (using 4.3.2 (KDE 4.3.2), Debian packages)
Compiler:          cc
OS:                Linux (i686) release 2.6.30-2-686-bigmem

Currently amarok handles copy to collection commands in parallel. This means, for every file in the requested artist / album, a KIO slave is launched and all KIO slaves do their jobs is in parallel but this behaviour is not optimal from many perspectives:

- USB flash media doesn't like parallel operations, multiple files at the same time and grinds to a halt. Speeds drop to KBytes then sometimes Bytes if you have enough files. As a result, I need to wait much much more for copying an album to my iPod or USB Stick (which I use with my Hi-Fi)

- Since files are copied with race conditions, their completion order is randomized at best case. My Hi-Fi that I use regularly likes to play first copied file first. This means my playlists can preserve its order without a playlist file. But with this setup, also this feature is lost, which is a bad side effect.

For the performance and other implicit benefits, KIO Slaves should run in parallel.

To Reproduce: 
1- Copy an album (or several albums) to a USB Flash Disk / iPod / similar device. 

2- Running "ps aux |grep amarok" will reveal several KIO processes & iotop/iostat will show disasterously slow transfer speeds.

3- If a song ends during copying your playback will be stuck, playback controls will not work.

4- After some time and some stressing to your memory device, your transfers will eventually finish.

5- Everyting will return to normal.

Expected behaviour: In step 1/2, amarok should queue the transfers and should run them sequentially.

Note: UI freeze is a bug and doesn't belong to this entry, I'll file a new one.
Comment 1 Hakan Bayindir 2009-12-10 20:31:34 UTC
(In reply to comment #0)
> For the performance and other implicit benefits, KIO Slaves should run in
> parallel.
Correct version: For the performance and other implicit benefits, KIO Slaves should not run in parallel.
Comment 2 Myriam Schweingruber 2009-12-10 21:20:57 UTC

*** This bug has been marked as a duplicate of bug 206550 ***
Comment 3 Hakan Bayindir 2009-12-10 22:59:24 UTC
IMHO, this bug is not a duplicate of bug 206550 because 206550 discusses the problem of two sequential copy-to operations but this bug discusses the problem inside of one copy-to operation.

In other terms, 206550 says more than one copy-to operation fails,
This bug says that copy to tries to parallelize the file copy within single copy-to action so performance degrades extremely.
Comment 4 Kristjan Ugrin 2009-12-13 13:53:19 UTC
I can confirm that - copying 100 files to USB player just stalled with a lot of
kio_file processes in background.
A good solution would be to implement queueing in old amarok - every single file processed individually and in case of a failure while transferring a report would be generated.
Comment 5 Hakan Bayindir 2009-12-25 21:57:10 UTC
Created attachment 39338 [details]
Output of "ps aux |grep amarok" on my computer during a big copy to collection operation of several albums
Comment 6 Myriam Schweingruber 2009-12-26 00:51:27 UTC
Changing element
Comment 7 Casey Link 2010-01-08 20:22:55 UTC
Hey folks,

I need some more information before I can begin tracking down this bug. Could you all please post the exact devices you are transferring tracks to when you experience this bug?
Comment 8 Casey Link 2010-01-08 21:22:17 UTC
Was able to test with a generic USB key I have. Amarok was running 150 jobs at once, but I changed this to 1.

After doing so I no longer experience extremely slow write speeds.

However, this bug still needs to be tested against Ipods and MTP devices.

The commit should make it into master soon.
Comment 9 Myriam Schweingruber 2010-01-08 21:30:19 UTC
Changing to bugs list then.
Comment 10 Hakan Bayindir 2010-01-09 15:27:59 UTC
I'm experiencing this bug with an iPod and several MTPs (Hard disks and USB keys).
IMHO, Amarok copy files sequentially to every device for optimum performance.
Comment 11 Lenno Nagel 2010-01-28 12:05:39 UTC
I also ran into this bug when using an iPod Nano 2G with Amarok 2.2.2.

I selected about 12 albums (~150 tracks) and used the "Copy to collection >" feature, which then spawned ~150 kio_file threads, locking up the USB access and creatinga HUGE load average on the computer. I guess this is a pretty clear indication of the parallel access problem.

I hope the fix for this reaches a release pretty soon.. :)
Comment 12 Myriam Schweingruber 2010-02-07 14:35:23 UTC
Setting version correctly.
Comment 13 Casey Link 2010-02-07 17:13:45 UTC
commit 5d5e42c57b483c08c457a8b3570944a4906323de
Author: Casey Link <unnamedrambler@gmail.com>
Date:   Fri Jan 8 13:27:03 2010 -0700

    Change maximum concurrent UMS jobs to 1 (instead of 150), to prevent all the KIO jobs from stalling with dismal write speeds.
    Also, changed the initiation order of UMS devices slightly so the read capability is read at the beginning, this is allows
    the user to copy tracks to a UMS devices before scanning it (aka Read Device).
    CCBUG: 218152

diff --git a/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp b/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
index 1d9b5d0..b246613 100644
--- a/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
+++ b/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
@@ -309,6 +309,7 @@ MediaDeviceHandler::copyTrackListToDevice(const Meta::TrackList tracklist)
     DEBUG_BLOCK
 
     setupWriteCapability();
+    setupReadCapability();
 
     if( !m_wcb )
         return;
@@ -454,6 +455,7 @@ MediaDeviceHandler::copyTrackListToDevice(const Meta::TrackList tracklist)
 void
 MediaDeviceHandler::copyNextTrackToDevice()
 {
+    DEBUG_BLOCK
     Meta::TrackPtr track;
 
     // If there are more tracks to copy, copy the next one
@@ -1004,15 +1006,22 @@ MediaDeviceHandler::slotCopyTrackJobsDone( ThreadWeaver::Job* job )
 float
 MediaDeviceHandler::freeSpace() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
+    {
+        debug() << "totalCapacity:" << m_rcb->totalCapacity();
+        debug() << "usedCapacity():" << m_rcb->usedCapacity();
         return ( m_rcb->totalCapacity() - m_rcb->usedCapacity() );
-    else
+    } else {
+        debug() << "m_rcb null!";
         return 0.0;
+    }
 }
 
 float
 MediaDeviceHandler::usedcapacity() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
         return m_rcb->usedCapacity();
     else
@@ -1022,6 +1031,7 @@ MediaDeviceHandler::usedcapacity() const
 float
 MediaDeviceHandler::totalcapacity() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
         return m_rcb->totalCapacity();
     else
diff --git a/src/collection/umscollection/handler/UmsHandler.cpp b/src/collection/umscollection/handler/UmsHandler.cpp
index 047691c..17e18f8 100644
--- a/src/collection/umscollection/handler/UmsHandler.cpp
+++ b/src/collection/umscollection/handler/UmsHandler.cpp
@@ -65,6 +65,15 @@ using namespace Meta;
 
 /// UmsHandler
 
+// Define the maximum number of concurrent kio jobs allowed.
+// This used to be 150, but flash media doesn't handle parallel
+// writes well, so by forcing this constant to 1 the jobs
+// are run sequentially.
+// BUG: 218152
+#ifndef UMS_MAX_CONCURRENT_JOBS
+#define UMS_MAX_CONCURRENT_JOBS 1
+#endif
+
 UmsHandler::UmsHandler( UmsCollection *mc, const QString& mountPoint )
     : MediaDeviceHandler( mc )
     , m_watcher()
@@ -162,9 +171,35 @@ UmsHandler::init()
     m_parsed = false;
     m_parseAction = 0;
 
+    // Get storage access for getting device space capacity/usage
+    Solid::Device device = Solid::Device(  m_memColl->udi() );
+    if (  device.isValid() )
+    {
+        Solid::StorageAccess *storage = device.as<Solid::StorageAccess>();
+        if ( storage )
+            m_filepath = storage->filePath();
+        else if ( !m_mountPoint.isEmpty() )
+            m_filepath = m_mountPoint;
+
+        if ( !m_filepath.isEmpty() )
+            m_capacity = KDiskFreeSpaceInfo::freeSpaceInfo( m_filepath ).size();
+        else
+        {
+            debug() << "capacity = 0.0 because m_filepath.isEmpty()";
+            m_capacity = 0.0;
+        }
+    }
+    else
+    {
+        debug() << "device is not valid";
+        m_filepath = "";
+        m_capacity = 0.0;
+    }
+
     debug() << "Succeeded: true";
     m_memColl->emitCollectionReady();
     //m_memColl->slotAttemptConnectionDone( true );
+
 }
 
 void
@@ -475,7 +510,7 @@ UmsHandler::kioCopyTrack( const KUrl &src, const KUrl &dst )
     KIO::CopyJob *job = KIO::copy( src, dst, KIO::HideProgressInfo );
     m_jobcounter++;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
         copyNextTrackToDevice();
 
 
@@ -507,10 +542,10 @@ UmsHandler::fileTransferred( KJob *job )  //SLOT
         return;
     }
 
-    // Limit max number of jobs to 150, make sure more tracks left
+    // Limit max number of jobs to 1, make sure more tracks left
     // to copy
     debug() << "Tracks to copy still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < 1 )
     {
         debug() << "Jobs: " << m_jobcounter;
         copyNextTrackToDevice();
@@ -549,7 +584,7 @@ UmsHandler::deleteFile( const KUrl &url )
 
     m_jobcounter++;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
         removeNextTrackFromDevice();
 
     connect( job, SIGNAL( result( KJob * ) ),
@@ -567,10 +602,8 @@ UmsHandler::fileDeleted( KJob *job )  //SLOT
 
     m_jobcounter--;
 
-    // Limit max number of jobs to 150, make sure more tracks left
-    // to delete
     debug() << "Tracks to delete still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
     {
         debug() << "Jobs: " << m_jobcounter;
         removeNextTrackFromDevice();
@@ -638,6 +671,7 @@ UmsHandler::usedCapacity() const
 float
 UmsHandler::totalCapacity() const
 {
+    DEBUG_BLOCK
     return m_capacity;
 }
 
@@ -654,28 +688,6 @@ UmsHandler::prepareToParseTracks()
 {
     DEBUG_BLOCK
 
-    // Get storage access for getting device space capacity/usage
-
-    Solid::Device device = Solid::Device(  m_memColl->udi() );
-    if (  device.isValid() )
-    {
-        Solid::StorageAccess *storage = device.as<Solid::StorageAccess>();
-        if ( storage )
-            m_filepath = storage->filePath();
-        else if ( !m_mountPoint.isEmpty() )
-            m_filepath = m_mountPoint;
-
-        if ( !m_filepath.isEmpty() )
-            m_capacity = KDiskFreeSpaceInfo::freeSpaceInfo( m_filepath ).size();
-        else
-            m_capacity = 0.0;
-    }
-    else
-    {
-        m_filepath = "";
-        m_capacity = 0.0;
-    }
-
     m_watcher.addDir( m_mountPoint, KDirWatch::WatchDirOnly | KDirWatch::WatchFiles | KDirWatch::WatchSubDirs );
 
     QDirIterator it( m_mountPoint, QDirIterator::Subdirectories );
diff --git a/src/dialogs/OrganizeCollectionDialog.cpp b/src/dialogs/OrganizeCollectionDialog.cpp
index 39bb9e6..fac12c5 100644
--- a/src/dialogs/OrganizeCollectionDialog.cpp
+++ b/src/dialogs/OrganizeCollectionDialog.cpp
@@ -28,6 +28,8 @@
 #include "QStringx.h"
 #include "ui_OrganizeCollectionDialogBase.h"
 
+#include <kcolorscheme.h>
+
 #include <QDir>
 
 OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &tracks, const QStringList &folders, QWidget *parent,  const char *name, bool modal,
@@ -57,37 +59,41 @@ OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &track
     ui->setupUi( mainContainer );
 
     m_filenameLayoutDialog = new FilenameLayoutDialog( mainContainer, 1 );   //", 1" means isOrganizeCollection ==> doesn't show Options frame
-    m_filenameLayoutDialog->hide();
+//    m_filenameLayoutDialog->hide();
     connect( this, SIGNAL( accepted() ),
              m_filenameLayoutDialog, SLOT( onAccept() ) );
-    ui->verticalLayout->insertWidget( 3, m_filenameLayoutDialog );
+    ui->verticalLayout->insertWidget( 1, m_filenameLayoutDialog );
     ui->ignoreTheCheck->show();
 
     ui->folderCombo->insertItems( 0, folders );
     ui->folderCombo->setCurrentIndex( AmarokConfig::organizeDirectory() );
     ui->overwriteCheck->setChecked( AmarokConfig::overwriteFiles() );
-    ui->filetypeCheck->setChecked( AmarokConfig::groupByFiletype() );
-    ui->initialCheck->setChecked( AmarokConfig::groupArtists() );
     ui->spaceCheck->setChecked( AmarokConfig::replaceSpace() );
     ui->ignoreTheCheck->setChecked( AmarokConfig::ignoreThe() );
     ui->vfatCheck->setChecked( AmarokConfig::vfatCompatible() );
     ui->asciiCheck->setChecked( AmarokConfig::asciiOnly() );
-    ui->customschemeCheck->setChecked( AmarokConfig::useCustomScheme() );
     ui->regexpEdit->setText( AmarokConfig::replacementRegexp() );
     ui->replaceEdit->setText( AmarokConfig::replacementString() );
 
+    ui->vfatCheck->hide(); // this is no longer an option, vfat safe is always enabled.
+
+    ui->previewTableWidget->horizontalHeader()->setResizeMode( QHeaderView::ResizeToContents );
+    ui->conflictLabel->hide();
+    QPalette p = ui->conflictLabel->palette();
+    KColorScheme::adjustForeground( p, KColorScheme::NegativeText ); // TODO this isn't working, the color is still normal
+    ui->conflictLabel->setPalette( p );
+
+    // to show the conflict error
+    connect( ui->overwriteCheck, SIGNAL( stateChanged( int ) ), SLOT( slotUpdatePreview() ) );
+
     connect( this, SIGNAL( updatePreview( QString ) ), ui->previewText, SLOT( setText( QString ) ) );
 
-    connect( ui->filetypeCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
-    connect( ui->initialCheck  , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->ignoreTheCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->spaceCheck    , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->asciiCheck    , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
-    connect( ui->customschemeCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->regexpEdit    , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) );
     connect( ui->replaceEdit    , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) );
     connect( m_filenameLayoutDialog, SIGNAL( schemeChanged() ), this, SLOT( slotUpdatePreview() ) );
-    connect( ui->customschemeCheck, SIGNAL( toggled( bool ) ), this, SLOT( toggleCustomScheme( bool ) ) );
 
     connect( this , SIGNAL( accepted() ), SLOT( slotDialogAccepted() ) );
     connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ),
@@ -95,7 +101,6 @@ OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &track
     connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ),
              this, SLOT( slotEnableOk( const QString & ) ) );
 
-    toggleCustomScheme( ui->customschemeCheck->isChecked() );
     slotEnableOk( ui->folderCombo->currentText() );
 
     init();
@@ -229,29 +234,32 @@ OrganizeCollectionDialog::buildFormatTip() const
 QString
 OrganizeCollectionDialog::buildFormatString() const
 {
-    if( ui->customschemeCheck->isChecked() )
-        return "%folder/" + m_filenameLayoutDialog->getParsableScheme() + ".%filetype";
-    QString format = "%folder/";
-    if( ui->filetypeCheck->isChecked() )
-        format += "%filetype/";
-    if( ui->initialCheck->isChecked() )
-        format += "%initial/";
-
-    format += "%albumartist/";
-    if( ui->spaceCheck->isChecked() )   //replace spaces with underscores
-    {
-        format += "%album{_(Disc_%discnumber)}/";
-        format += "{%track_-_}%title.%filetype";
-    }
-    else
+    return "%folder/" + m_filenameLayoutDialog->getParsableScheme() + ".%filetype";
+}
+
+QString
+OrganizeCollectionDialog::commonPrefix( const QStringList &list ) const
+{
+    QString option = list.first().toLower();
+    int length = option.length();
+    while( length > 0 )
     {
-        format += "%album{ (Disc %discnumber)}/";
-        format += "{%track - }%title.%filetype";
+        bool found = true;
+        foreach( QString string, list )
+        {
+            if( string.left(length).toLower() != option )
+            {
+                found = false;
+                break;
+            }
+        }
+        if( found )
+            break;
+        --length;
+        option = option.left( length );
     }
+    return option;
 
-
-    format = QDir::fromNativeSeparators( format );      //fromNativeSeparators handles \\ under windows
-    return format;
 }
 
 
@@ -259,8 +267,56 @@ OrganizeCollectionDialog::buildFormatString() const
 void
 OrganizeCollectionDialog::preview( const QString &format )
 {
-    if( m_previewTrack )
-        emit updatePreview( buildDestination( format, m_previewTrack ) );
+    DEBUG_BLOCK
+    /*if( m_previewTrack )
+        emit updatePreview( buildDestination( format, m_previewTrack ) );*/
+
+    ui->previewTableWidget->clearContents();
+    ui->previewTableWidget->setRowCount( m_allTracks.size() );
+    bool conflict = false;
+    for (int i = 0; i < m_allTracks.size(); ++i)
+    {
+        Meta::TrackPtr track = m_allTracks.at(i);
+
+        QString originalPath = track->prettyUrl();
+        QString newPath = buildDestination( format, track );
+
+//         QStringList list;
+//         list << originalPath << newPath;
+//
+//         QString common_prefix = commonPrefix( list );
+//         debug() << "common prefix: " << common_prefix;
+//         originalPath = originalPath.mid( common_prefix.length() );
+//         newPath = newPath.mid( common_prefix.length() );
+
+        QFileInfo info( newPath );
+        if( !conflict  && info.exists() )
+            conflict = true;
+
+        QTableWidgetItem* item = new QTableWidgetItem( originalPath );
+        ui->previewTableWidget->setItem(i, 0, item);
+        QPalette p = ui->previewTableWidget->palette();
+        KColorScheme::adjustBackground(p, KColorScheme::NegativeBackground);
+        if( info.exists() )
+        {
+
+            item->setBackgroundColor( p.color( QPalette::Base ) );
+        }
+
+        item = new QTableWidgetItem( newPath );
+        if( info.exists() )
+            item->setBackgroundColor( p.color( QPalette::Base ) );
+        ui->previewTableWidget->setItem(i, 1, item);
+
+    }
+    if( conflict )
+    {
+        ui->conflictLabel->show();
+        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." ) );
+    }
 }
 
 
@@ -281,9 +337,9 @@ OrganizeCollectionDialog::cleanPath( const QString &component ) const
     result.simplified();
     if( ui->spaceCheck->isChecked() )
         result.replace( QRegExp( "\\s" ), "_" );
-    debug()<<"I'm about to do Amarok::vfatPath( result ), this is result: "<<result;
-    if( ui->vfatCheck->isChecked() )
-        result = Amarok::vfatPath( result );
+//     debug()<<"I'm about to do Amarok::vfatPath( result ), this is result: "<<result;
+//    if( ui->vfatCheck->isChecked() )
+    result = Amarok::vfatPath( result );
 
     result.replace( '/', '-' );
 
@@ -298,10 +354,7 @@ OrganizeCollectionDialog::update( int dummy )   //why the dummy?
 
     if( m_previewTrack )
     {
-        if( ui->customschemeCheck->isChecked() )
-            emit updatePreview( buildDestination( "%folder/" + m_filenameLayoutDialog->getParsableScheme(), m_previewTrack ) );
-        else
-            emit updatePreview( buildDestination( buildFormatString(), m_previewTrack ) );
+        emit updatePreview( buildDestination( "%folder/" + m_filenameLayoutDialog->getParsableScheme(), m_previewTrack ) );
     }
 }
 
@@ -330,35 +383,14 @@ void
 OrganizeCollectionDialog::slotDialogAccepted()
 {
     AmarokConfig::setOrganizeDirectory( ui->folderCombo->currentIndex() );
-    AmarokConfig::setGroupByFiletype( ui->filetypeCheck->isChecked() );
-    AmarokConfig::setGroupArtists( ui->initialCheck->isChecked() );
     AmarokConfig::setIgnoreThe( ui->ignoreTheCheck->isChecked() );
     AmarokConfig::setReplaceSpace( ui->spaceCheck->isChecked() );
     AmarokConfig::setVfatCompatible( ui->vfatCheck->isChecked() );
     AmarokConfig::setAsciiOnly( ui->asciiCheck->isChecked() );
-    AmarokConfig::setUseCustomScheme( ui->customschemeCheck->isChecked() );
     AmarokConfig::setReplacementRegexp( ui->regexpEdit->text() );
     AmarokConfig::setReplacementString( ui->replaceEdit->text() );
 }
 
-//Hides and shows the right elements in the interface in the right order to keep the layout sane
-void
-OrganizeCollectionDialog::toggleCustomScheme( bool state )  //SLOT
-{
-    if( state )
-    {
-        ui->initialCheck->hide();
-        ui->filetypeCheck->hide();
-        m_filenameLayoutDialog->setVisible( true );
-    }
-    else
-    {
-        m_filenameLayoutDialog->hide();
-        ui->initialCheck->show();
-        ui->filetypeCheck->show();
-    }
-}
-
 //The Ok button should be disabled when there's no collection root selected, and when there is no .%filetype in format string
 void
 OrganizeCollectionDialog::slotEnableOk( const QString & currentCollectionRoot )
diff --git a/src/dialogs/OrganizeCollectionDialog.h b/src/dialogs/OrganizeCollectionDialog.h
index a5a8fab..cadeb7b 100644
--- a/src/dialogs/OrganizeCollectionDialog.h
+++ b/src/dialogs/OrganizeCollectionDialog.h
@@ -60,6 +60,7 @@ class AMAROK_EXPORT OrganizeCollectionDialog : public KDialog
         QString cleanPath( const QString &component ) const;
         QString buildFormatTip() const;
         QString buildFormatString() const;
+        QString commonPrefix( const QStringList &list ) const;
         void toggleDetails();
         void preview( const QString &format );
         void update( int dummy );
@@ -73,7 +74,6 @@ class AMAROK_EXPORT OrganizeCollectionDialog : public KDialog
         Meta::TrackList m_allTracks;
 
     private slots:
-        void toggleCustomScheme( bool state );
         void slotEnableOk( const QString & currentCollectionRoot );
 };
Comment 14 Casey Link 2010-02-07 17:16:02 UTC
As of commit 5d5e42c57b483c08c457a8b3570944a4906323de  this bug should be resolved for UMS devices. MTP and iPods still suffer from it. Could someoneverify this please?
Comment 15 Casey Link 2010-02-08 00:34:35 UTC
commit 5d5e42c57b483c08c457a8b3570944a4906323de
Author: Casey Link <unnamedrambler@gmail.com>
Date:   Fri Jan 8 13:27:03 2010 -0700

    Change maximum concurrent UMS jobs to 1 (instead of 150), to prevent all the KIO jobs from stalling with dismal write speeds.
    Also, changed the initiation order of UMS devices slightly so the read capability is read at the beginning, this is allows
    the user to copy tracks to a UMS devices before scanning it (aka Read Device).
    CCBUG: 218152

diff --git a/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp b/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
index 1d9b5d0..b246613 100644
--- a/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
+++ b/src/collection/mediadevicecollection/handler/MediaDeviceHandler.cpp
@@ -309,6 +309,7 @@ MediaDeviceHandler::copyTrackListToDevice(const Meta::TrackList tracklist)
     DEBUG_BLOCK
 
     setupWriteCapability();
+    setupReadCapability();
 
     if( !m_wcb )
         return;
@@ -454,6 +455,7 @@ MediaDeviceHandler::copyTrackListToDevice(const Meta::TrackList tracklist)
 void
 MediaDeviceHandler::copyNextTrackToDevice()
 {
+    DEBUG_BLOCK
     Meta::TrackPtr track;
 
     // If there are more tracks to copy, copy the next one
@@ -1004,15 +1006,22 @@ MediaDeviceHandler::slotCopyTrackJobsDone( ThreadWeaver::Job* job )
 float
 MediaDeviceHandler::freeSpace() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
+    {
+        debug() << "totalCapacity:" << m_rcb->totalCapacity();
+        debug() << "usedCapacity():" << m_rcb->usedCapacity();
         return ( m_rcb->totalCapacity() - m_rcb->usedCapacity() );
-    else
+    } else {
+        debug() << "m_rcb null!";
         return 0.0;
+    }
 }
 
 float
 MediaDeviceHandler::usedcapacity() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
         return m_rcb->usedCapacity();
     else
@@ -1022,6 +1031,7 @@ MediaDeviceHandler::usedcapacity() const
 float
 MediaDeviceHandler::totalcapacity() const
 {
+    DEBUG_BLOCK
     if ( m_rcb )
         return m_rcb->totalCapacity();
     else
diff --git a/src/collection/umscollection/handler/UmsHandler.cpp b/src/collection/umscollection/handler/UmsHandler.cpp
index 047691c..17e18f8 100644
--- a/src/collection/umscollection/handler/UmsHandler.cpp
+++ b/src/collection/umscollection/handler/UmsHandler.cpp
@@ -65,6 +65,15 @@ using namespace Meta;
 
 /// UmsHandler
 
+// Define the maximum number of concurrent kio jobs allowed.
+// This used to be 150, but flash media doesn't handle parallel
+// writes well, so by forcing this constant to 1 the jobs
+// are run sequentially.
+// BUG: 218152
+#ifndef UMS_MAX_CONCURRENT_JOBS
+#define UMS_MAX_CONCURRENT_JOBS 1
+#endif
+
 UmsHandler::UmsHandler( UmsCollection *mc, const QString& mountPoint )
     : MediaDeviceHandler( mc )
     , m_watcher()
@@ -162,9 +171,35 @@ UmsHandler::init()
     m_parsed = false;
     m_parseAction = 0;
 
+    // Get storage access for getting device space capacity/usage
+    Solid::Device device = Solid::Device(  m_memColl->udi() );
+    if (  device.isValid() )
+    {
+        Solid::StorageAccess *storage = device.as<Solid::StorageAccess>();
+        if ( storage )
+            m_filepath = storage->filePath();
+        else if ( !m_mountPoint.isEmpty() )
+            m_filepath = m_mountPoint;
+
+        if ( !m_filepath.isEmpty() )
+            m_capacity = KDiskFreeSpaceInfo::freeSpaceInfo( m_filepath ).size();
+        else
+        {
+            debug() << "capacity = 0.0 because m_filepath.isEmpty()";
+            m_capacity = 0.0;
+        }
+    }
+    else
+    {
+        debug() << "device is not valid";
+        m_filepath = "";
+        m_capacity = 0.0;
+    }
+
     debug() << "Succeeded: true";
     m_memColl->emitCollectionReady();
     //m_memColl->slotAttemptConnectionDone( true );
+
 }
 
 void
@@ -475,7 +510,7 @@ UmsHandler::kioCopyTrack( const KUrl &src, const KUrl &dst )
     KIO::CopyJob *job = KIO::copy( src, dst, KIO::HideProgressInfo );
     m_jobcounter++;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
         copyNextTrackToDevice();
 
 
@@ -507,10 +542,10 @@ UmsHandler::fileTransferred( KJob *job )  //SLOT
         return;
     }
 
-    // Limit max number of jobs to 150, make sure more tracks left
+    // Limit max number of jobs to 1, make sure more tracks left
     // to copy
     debug() << "Tracks to copy still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < 1 )
     {
         debug() << "Jobs: " << m_jobcounter;
         copyNextTrackToDevice();
@@ -549,7 +584,7 @@ UmsHandler::deleteFile( const KUrl &url )
 
     m_jobcounter++;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
         removeNextTrackFromDevice();
 
     connect( job, SIGNAL( result( KJob * ) ),
@@ -567,10 +602,8 @@ UmsHandler::fileDeleted( KJob *job )  //SLOT
 
     m_jobcounter--;
 
-    // Limit max number of jobs to 150, make sure more tracks left
-    // to delete
     debug() << "Tracks to delete still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < UMS_MAX_CONCURRENT_JOBS )
     {
         debug() << "Jobs: " << m_jobcounter;
         removeNextTrackFromDevice();
@@ -638,6 +671,7 @@ UmsHandler::usedCapacity() const
 float
 UmsHandler::totalCapacity() const
 {
+    DEBUG_BLOCK
     return m_capacity;
 }
 
@@ -654,28 +688,6 @@ UmsHandler::prepareToParseTracks()
 {
     DEBUG_BLOCK
 
-    // Get storage access for getting device space capacity/usage
-
-    Solid::Device device = Solid::Device(  m_memColl->udi() );
-    if (  device.isValid() )
-    {
-        Solid::StorageAccess *storage = device.as<Solid::StorageAccess>();
-        if ( storage )
-            m_filepath = storage->filePath();
-        else if ( !m_mountPoint.isEmpty() )
-            m_filepath = m_mountPoint;
-
-        if ( !m_filepath.isEmpty() )
-            m_capacity = KDiskFreeSpaceInfo::freeSpaceInfo( m_filepath ).size();
-        else
-            m_capacity = 0.0;
-    }
-    else
-    {
-        m_filepath = "";
-        m_capacity = 0.0;
-    }
-
     m_watcher.addDir( m_mountPoint, KDirWatch::WatchDirOnly | KDirWatch::WatchFiles | KDirWatch::WatchSubDirs );
 
     QDirIterator it( m_mountPoint, QDirIterator::Subdirectories );
diff --git a/src/dialogs/OrganizeCollectionDialog.cpp b/src/dialogs/OrganizeCollectionDialog.cpp
index 39bb9e6..fac12c5 100644
--- a/src/dialogs/OrganizeCollectionDialog.cpp
+++ b/src/dialogs/OrganizeCollectionDialog.cpp
@@ -28,6 +28,8 @@
 #include "QStringx.h"
 #include "ui_OrganizeCollectionDialogBase.h"
 
+#include <kcolorscheme.h>
+
 #include <QDir>
 
 OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &tracks, const QStringList &folders, QWidget *parent,  const char *name, bool modal,
@@ -57,37 +59,41 @@ OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &track
     ui->setupUi( mainContainer );
 
     m_filenameLayoutDialog = new FilenameLayoutDialog( mainContainer, 1 );   //", 1" means isOrganizeCollection ==> doesn't show Options frame
-    m_filenameLayoutDialog->hide();
+//    m_filenameLayoutDialog->hide();
     connect( this, SIGNAL( accepted() ),
              m_filenameLayoutDialog, SLOT( onAccept() ) );
-    ui->verticalLayout->insertWidget( 3, m_filenameLayoutDialog );
+    ui->verticalLayout->insertWidget( 1, m_filenameLayoutDialog );
     ui->ignoreTheCheck->show();
 
     ui->folderCombo->insertItems( 0, folders );
     ui->folderCombo->setCurrentIndex( AmarokConfig::organizeDirectory() );
     ui->overwriteCheck->setChecked( AmarokConfig::overwriteFiles() );
-    ui->filetypeCheck->setChecked( AmarokConfig::groupByFiletype() );
-    ui->initialCheck->setChecked( AmarokConfig::groupArtists() );
     ui->spaceCheck->setChecked( AmarokConfig::replaceSpace() );
     ui->ignoreTheCheck->setChecked( AmarokConfig::ignoreThe() );
     ui->vfatCheck->setChecked( AmarokConfig::vfatCompatible() );
     ui->asciiCheck->setChecked( AmarokConfig::asciiOnly() );
-    ui->customschemeCheck->setChecked( AmarokConfig::useCustomScheme() );
     ui->regexpEdit->setText( AmarokConfig::replacementRegexp() );
     ui->replaceEdit->setText( AmarokConfig::replacementString() );
 
+    ui->vfatCheck->hide(); // this is no longer an option, vfat safe is always enabled.
+
+    ui->previewTableWidget->horizontalHeader()->setResizeMode( QHeaderView::ResizeToContents );
+    ui->conflictLabel->hide();
+    QPalette p = ui->conflictLabel->palette();
+    KColorScheme::adjustForeground( p, KColorScheme::NegativeText ); // TODO this isn't working, the color is still normal
+    ui->conflictLabel->setPalette( p );
+
+    // to show the conflict error
+    connect( ui->overwriteCheck, SIGNAL( stateChanged( int ) ), SLOT( slotUpdatePreview() ) );
+
     connect( this, SIGNAL( updatePreview( QString ) ), ui->previewText, SLOT( setText( QString ) ) );
 
-    connect( ui->filetypeCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
-    connect( ui->initialCheck  , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->ignoreTheCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->spaceCheck    , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->asciiCheck    , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
-    connect( ui->customschemeCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) );
     connect( ui->regexpEdit    , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) );
     connect( ui->replaceEdit    , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) );
     connect( m_filenameLayoutDialog, SIGNAL( schemeChanged() ), this, SLOT( slotUpdatePreview() ) );
-    connect( ui->customschemeCheck, SIGNAL( toggled( bool ) ), this, SLOT( toggleCustomScheme( bool ) ) );
 
     connect( this , SIGNAL( accepted() ), SLOT( slotDialogAccepted() ) );
     connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ),
@@ -95,7 +101,6 @@ OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &track
     connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ),
              this, SLOT( slotEnableOk( const QString & ) ) );
 
-    toggleCustomScheme( ui->customschemeCheck->isChecked() );
     slotEnableOk( ui->folderCombo->currentText() );
 
     init();
@@ -229,29 +234,32 @@ OrganizeCollectionDialog::buildFormatTip() const
 QString
 OrganizeCollectionDialog::buildFormatString() const
 {
-    if( ui->customschemeCheck->isChecked() )
-        return "%folder/" + m_filenameLayoutDialog->getParsableScheme() + ".%filetype";
-    QString format = "%folder/";
-    if( ui->filetypeCheck->isChecked() )
-        format += "%filetype/";
-    if( ui->initialCheck->isChecked() )
-        format += "%initial/";
-
-    format += "%albumartist/";
-    if( ui->spaceCheck->isChecked() )   //replace spaces with underscores
-    {
-        format += "%album{_(Disc_%discnumber)}/";
-        format += "{%track_-_}%title.%filetype";
-    }
-    else
+    return "%folder/" + m_filenameLayoutDialog->getParsableScheme() + ".%filetype";
+}
+
+QString
+OrganizeCollectionDialog::commonPrefix( const QStringList &list ) const
+{
+    QString option = list.first().toLower();
+    int length = option.length();
+    while( length > 0 )
     {
-        format += "%album{ (Disc %discnumber)}/";
-        format += "{%track - }%title.%filetype";
+        bool found = true;
+        foreach( QString string, list )
+        {
+            if( string.left(length).toLower() != option )
+            {
+                found = false;
+                break;
+            }
+        }
+        if( found )
+            break;
+        --length;
+        option = option.left( length );
     }
+    return option;
 
-
-    format = QDir::fromNativeSeparators( format );      //fromNativeSeparators handles \\ under windows
-    return format;
 }
 
 
@@ -259,8 +267,56 @@ OrganizeCollectionDialog::buildFormatString() const
 void
 OrganizeCollectionDialog::preview( const QString &format )
 {
-    if( m_previewTrack )
-        emit updatePreview( buildDestination( format, m_previewTrack ) );
+    DEBUG_BLOCK
+    /*if( m_previewTrack )
+        emit updatePreview( buildDestination( format, m_previewTrack ) );*/
+
+    ui->previewTableWidget->clearContents();
+    ui->previewTableWidget->setRowCount( m_allTracks.size() );
+    bool conflict = false;
+    for (int i = 0; i < m_allTracks.size(); ++i)
+    {
+        Meta::TrackPtr track = m_allTracks.at(i);
+
+        QString originalPath = track->prettyUrl();
+        QString newPath = buildDestination( format, track );
+
+//         QStringList list;
+//         list << originalPath << newPath;
+//
+//         QString common_prefix = commonPrefix( list );
+//         debug() << "common prefix: " << common_prefix;
+//         originalPath = originalPath.mid( common_prefix.length() );
+//         newPath = newPath.mid( common_prefix.length() );
+
+        QFileInfo info( newPath );
+        if( !conflict  && info.exists() )
+            conflict = true;
+
+        QTableWidgetItem* item = new QTableWidgetItem( originalPath );
+        ui->previewTableWidget->setItem(i, 0, item);
+        QPalette p = ui->previewTableWidget->palette();
+        KColorScheme::adjustBackground(p, KColorScheme::NegativeBackground);
+        if( info.exists() )
+        {
+
+            item->setBackgroundColor( p.color( QPalette::Base ) );
+        }
+
+        item = new QTableWidgetItem( newPath );
+        if( info.exists() )
+            item->setBackgroundColor( p.color( QPalette::Base ) );
+        ui->previewTableWidget->setItem(i, 1, item);
+
+    }
+    if( conflict )
+    {
+        ui->conflictLabel->show();
+        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." ) );
+    }
 }
 
 
@@ -281,9 +337,9 @@ OrganizeCollectionDialog::cleanPath( const QString &component ) const
     result.simplified();
     if( ui->spaceCheck->isChecked() )
         result.replace( QRegExp( "\\s" ), "_" );
-    debug()<<"I'm about to do Amarok::vfatPath( result ), this is result: "<<result;
-    if( ui->vfatCheck->isChecked() )
-        result = Amarok::vfatPath( result );
+//     debug()<<"I'm about to do Amarok::vfatPath( result ), this is result: "<<result;
+//    if( ui->vfatCheck->isChecked() )
+    result = Amarok::vfatPath( result );
 
     result.replace( '/', '-' );
 
@@ -298,10 +354,7 @@ OrganizeCollectionDialog::update( int dummy )   //why the dummy?
 
     if( m_previewTrack )
     {
-        if( ui->customschemeCheck->isChecked() )
-            emit updatePreview( buildDestination( "%folder/" + m_filenameLayoutDialog->getParsableScheme(), m_previewTrack ) );
-        else
-            emit updatePreview( buildDestination( buildFormatString(), m_previewTrack ) );
+        emit updatePreview( buildDestination( "%folder/" + m_filenameLayoutDialog->getParsableScheme(), m_previewTrack ) );
     }
 }
 
@@ -330,35 +383,14 @@ void
 OrganizeCollectionDialog::slotDialogAccepted()
 {
     AmarokConfig::setOrganizeDirectory( ui->folderCombo->currentIndex() );
-    AmarokConfig::setGroupByFiletype( ui->filetypeCheck->isChecked() );
-    AmarokConfig::setGroupArtists( ui->initialCheck->isChecked() );
     AmarokConfig::setIgnoreThe( ui->ignoreTheCheck->isChecked() );
     AmarokConfig::setReplaceSpace( ui->spaceCheck->isChecked() );
     AmarokConfig::setVfatCompatible( ui->vfatCheck->isChecked() );
     AmarokConfig::setAsciiOnly( ui->asciiCheck->isChecked() );
-    AmarokConfig::setUseCustomScheme( ui->customschemeCheck->isChecked() );
     AmarokConfig::setReplacementRegexp( ui->regexpEdit->text() );
     AmarokConfig::setReplacementString( ui->replaceEdit->text() );
 }
 
-//Hides and shows the right elements in the interface in the right order to keep the layout sane
-void
-OrganizeCollectionDialog::toggleCustomScheme( bool state )  //SLOT
-{
-    if( state )
-    {
-        ui->initialCheck->hide();
-        ui->filetypeCheck->hide();
-        m_filenameLayoutDialog->setVisible( true );
-    }
-    else
-    {
-        m_filenameLayoutDialog->hide();
-        ui->initialCheck->show();
-        ui->filetypeCheck->show();
-    }
-}
-
 //The Ok button should be disabled when there's no collection root selected, and when there is no .%filetype in format string
 void
 OrganizeCollectionDialog::slotEnableOk( const QString & currentCollectionRoot )
diff --git a/src/dialogs/OrganizeCollectionDialog.h b/src/dialogs/OrganizeCollectionDialog.h
index a5a8fab..cadeb7b 100644
--- a/src/dialogs/OrganizeCollectionDialog.h
+++ b/src/dialogs/OrganizeCollectionDialog.h
@@ -60,6 +60,7 @@ class AMAROK_EXPORT OrganizeCollectionDialog : public KDialog
         QString cleanPath( const QString &component ) const;
         QString buildFormatTip() const;
         QString buildFormatString() const;
+        QString commonPrefix( const QStringList &list ) const;
         void toggleDetails();
         void preview( const QString &format );
         void update( int dummy );
@@ -73,7 +74,6 @@ class AMAROK_EXPORT OrganizeCollectionDialog : public KDialog
         Meta::TrackList m_allTracks;
 
     private slots:
-        void toggleCustomScheme( bool state );
         void slotEnableOk( const QString & currentCollectionRoot );
 };
Comment 16 Hakan Bayindir 2010-03-18 20:34:20 UTC
Hello,

I updated to amarok 2.3.0 today and while the bug seems to be vanished for universal media storage class (flash disks and similar devices), the problem persists for iPods.
Comment 17 Sven Krohlas 2010-03-25 21:12:47 UTC
*** Bug 232144 has been marked as a duplicate of this bug. ***
Comment 18 Casey Link 2010-03-27 23:34:04 UTC
commit 32f0246e55029c4703c283519396d0f764f24e29
Author: Casey Link <unnamedrambler@gmail.com>
Date:   Sat Mar 27 18:06:08 2010 -0400

    Limit the max number of concurrent transfer jobs to ipods to 1, that is,
    only one transfer job can be active at once. This should remedy the
    problem of 150 kio jobs being started and slowing disk i/o to a crawl.
    CCBUG: 218152

diff --git a/src/collection/ipodcollection/handler/IpodHandler.cpp b/src/collection/ipodcollection/handler/IpodHandler.cpp
index c86119a..f3563fc 100644
--- a/src/collection/ipodcollection/handler/IpodHandler.cpp
+++ b/src/collection/ipodcollection/handler/IpodHandler.cpp
@@ -68,6 +68,14 @@ extern "C" {
 using namespace Meta;
 
 /// IpodHandler
+// Define the maximum number of concurrent kio jobs allowed.
+// This used to be 150, but ipods don't handle parallel
+// writes well, so by forcing this constant to 1 the jobs
+// are run sequentially.
+// BUG: 218152
+#ifndef IPOD_MAX_CONCURRENT_JOBS
+#define IPOD_MAX_CONCURRENT_JOBS 1
+#endif
 
 IpodHandler::IpodHandler( IpodCollection *mc, const IpodDeviceInfo *deviceInfo )
     : MediaDeviceHandler( mc )
@@ -1276,9 +1284,9 @@ IpodHandler::kioCopyTrack( const KUrl &src, const KUrl &dst )
 
     KIO::CopyJob *job = KIO::copy( src, dst, KIO::HideProgressInfo );
     job->setDefaultPermissions(true);
-    m_jobcounter++;
+    ++m_jobcounter;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < IPOD_MAX_CONCURRENT_JOBS )
         copyNextTrackToDevice();
 
 
@@ -1308,10 +1316,10 @@ IpodHandler::fileTransferred( KJob *job )  //SLOT
         return;
     }
 
-    // Limit max number of jobs to 150, make sure more tracks left
+    // Limit max number of jobs to IPOD_MAX_CONCURRENT_JOBS, make sure more tracks left
     // to copy
     debug() << "Tracks to copy still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < IPOD_MAX_CONCURRENT_JOBS )
     {
         debug() << "Jobs: " << m_jobcounter;
         copyNextTrackToDevice();
@@ -1344,7 +1352,7 @@ IpodHandler::deleteFile( const KUrl &url )
 
     m_jobcounter++;
 
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < IPOD_MAX_CONCURRENT_JOBS )
         removeNextTrackFromDevice();
 
     connect( job, SIGNAL( result( KJob * ) ),
@@ -1362,10 +1370,10 @@ IpodHandler::fileDeleted( KJob *job )  //SLOT
 
     m_jobcounter--;
 
-    // Limit max number of jobs to 150, make sure more tracks left
+    // Limit max number of jobs to IPOD_MAX_CONCURRENT_JOBS, make sure more tracks left
     // to delete
     debug() << "Tracks to delete still remain";
-    if( m_jobcounter < 150 )
+    if( m_jobcounter < IPOD_MAX_CONCURRENT_JOBS )
     {
         debug() << "Jobs: " << m_jobcounter;
         removeNextTrackFromDevice();
Comment 19 Sven Krohlas 2010-03-28 17:44:26 UTC
*** Bug 232486 has been marked as a duplicate of this bug. ***
Comment 20 Soren Harward 2010-05-10 02:38:21 UTC
Looks like this one is fixed.  We can re-open if there are still problems.
Comment 21 Hakan Bayindir 2010-05-10 08:44:34 UTC
The commit in this thread only fixes the bug for USB Mass Storage Devices. iPods and MTP devices still suffer.
Comment 22 Myriam Schweingruber 2010-05-22 12:27:25 UTC
Changing target.
Comment 23 Casey Link 2010-05-26 04:11:51 UTC
ipods and ums devices should no longer suffer from this bug.. as my above commits indicates.
Comment 24 Hakan Bayindir 2010-05-26 07:53:21 UTC
Yes, my bad. I've overlooked to the report and didn't see the new commit, will be more careful next time. I'll test the patch tonight and update the ticket.

Thanks for the heads up Casey.
Comment 25 Hakan Bayindir 2010-05-26 18:32:39 UTC
As of Amarok 2.3.1 Beta 1 (2.3.0.90?) the file copy speed for my iPod Nano 2G and USB sticks is nominal again. I'm closing this bug ticket. Sorry again for overlooking the last patch and reopening the ticket and the associated noise.