add mp3 and ogg torrent url info to JamendoAlbum
[amarok.git] / src / MediaDevice.cpp
blob1efad367479b484549ec0f5c436e90357a10e781
1 // (c) 2004 Christian Muehlhaeuser <chris@chris.de>
2 // (c) 2005-2006 Martin Aumueller <aumuell@reserv.at>
3 // (c) 2005 Seb Ruiz <ruiz@kde.org>
4 // (c) 2006 T.R.Shashwath <trshash84@gmail.com>
5 // (c) 2007 Jeff Mitchell <kde-dev@emailgoeshere.com>
6 // See COPYING file for licensing information
9 #define DEBUG_PREFIX "MediaDevice"
11 #include "MediaDevice.h"
13 #include "amarok.h"
14 #include "amarokconfig.h"
15 #include "app.h"
16 #include "collectiondb.h"
17 #include "debug.h"
18 #include "mediabrowser.h"
19 #include "MediaItem.h"
20 #include "metabundle.h"
21 #include "mountpointmanager.h"
22 #include "pluginmanager.h"
23 #include "podcastbundle.h"
24 #include "scriptmanager.h"
25 #include "statusbar.h"
27 #include <KIO/Job>
28 #include <KMessageBox>
30 MediaDevice::MediaDevice()
31 : Amarok::Plugin()
32 , m_name( QString() )
33 , m_hasMountPoint( true )
34 , m_autoDeletePodcasts( false )
35 , m_syncStats( false )
36 , m_transcode( false )
37 , m_transcodeAlways( false )
38 , m_transcodeRemove( false )
39 , sysProc ( 0 )
40 , m_parent( 0 )
41 , m_view( 0 )
42 , m_uid( QString() )
43 , m_deviceNode( QString() )
44 , m_mountPoint( QString() )
45 , m_fsType( QString() )
46 , m_wait( false )
47 , m_requireMount( false )
48 , m_canceled( false )
49 , m_transferring( false )
50 , m_deleting( false )
51 , m_deferredDisconnect( false )
52 , m_scheduledDisconnect( false )
53 , m_transfer( true )
54 , m_configure( true )
55 , m_customButton( false )
56 , m_playlistItem( 0 )
57 , m_podcastItem( 0 )
58 , m_invisibleItem( 0 )
59 , m_staleItem( 0 )
60 , m_orphanedItem( 0 )
62 sysProc = new K3ShellProcess(); Q_CHECK_PTR(sysProc);
65 void MediaDevice::init( MediaBrowser* parent )
67 m_parent = parent;
68 if( !m_view )
69 m_view = new MediaView( m_parent->m_views, this );
70 m_view->hide();
73 MediaDevice::~MediaDevice()
75 delete m_view;
76 delete sysProc;
79 bool
80 MediaDevice::isSpecialItem( MediaItem *item )
82 return (item == m_playlistItem) ||
83 (item == m_podcastItem) ||
84 (item == m_invisibleItem) ||
85 (item == m_staleItem) ||
86 (item == m_orphanedItem);
89 void
90 MediaDevice::loadConfig()
92 m_transcode = configBool( "Transcode" );
93 m_transcodeAlways = configBool( "TranscodeAlways" );
94 m_transcodeRemove = configBool( "TranscodeRemove" );
95 m_preconnectcmd = configString( "PreConnectCommand" );
96 if( m_preconnectcmd.isEmpty() )
97 m_preconnectcmd = configString( "MountCommand" );
98 m_postdisconnectcmd = configString( "PostDisconnectCommand" );
99 if( m_postdisconnectcmd.isEmpty() )
100 m_postdisconnectcmd = configString( "UmountCommand" );
101 if( m_requireMount && m_postdisconnectcmd.isEmpty() )
102 m_postdisconnectcmd = "kdeeject -q %d";
105 QString
106 MediaDevice::configString( const QString &name, const QString &defValue )
108 QString configName = "MediaDevice";
109 if( !uid().isEmpty() )
110 configName += '_' + uid();
111 KConfigGroup config = Amarok::config( configName );
112 return config.readEntry( name, defValue );
115 void
116 MediaDevice::setConfigString( const QString &name, const QString &value )
118 QString configName = "MediaDevice";
119 if( !uid().isEmpty() )
120 configName += '_' + uid();
121 KConfigGroup config = Amarok::config( configName );
122 config.writeEntry( name, value );
125 bool
126 MediaDevice::configBool( const QString &name, bool defValue )
128 QString configName = "MediaDevice";
129 if( !uid().isEmpty() )
130 configName += '_' + uid();
131 KConfigGroup config = Amarok::config( configName );
132 return config.readEntry( name, defValue );
135 void
136 MediaDevice::setConfigBool( const QString &name, bool value )
138 QString configName = "MediaDevice";
139 if( !uid().isEmpty() )
140 configName += '_' + uid();
141 KConfigGroup config = Amarok::config( configName );
142 config.writeEntry( name, value );
145 MediaView *
146 MediaDevice::view()
148 return m_view;
151 void
152 MediaDevice::hideProgress()
154 m_parent->m_progressBox->hide();
157 void
158 MediaDevice::updateRootItems()
160 if(m_podcastItem)
161 m_podcastItem->setVisible(m_podcastItem->childCount() > 0);
162 if(m_invisibleItem)
163 m_invisibleItem->setVisible(m_invisibleItem->childCount() > 0);
164 if(m_staleItem)
165 m_staleItem->setVisible(m_staleItem->childCount() > 0);
166 if(m_orphanedItem)
167 m_orphanedItem->setVisible(m_orphanedItem->childCount() > 0);
170 BundleList
171 MediaDevice::bundlesToSync( const QString &name, const KUrl &url )
173 //PORT 2.0
174 // BundleList bundles;
175 // if( !PlaylistFile::isPlaylistFile( url ) )
176 // {
177 // Amarok::StatusBar::instance()->longMessage( i18n( "Not a playlist file: %1", url.path() ),
178 // KDE::StatusBar::Sorry );
179 // return bundles;
180 // }
182 // PlaylistFile playlist( url.path() );
183 // if( playlist.isError() )
184 // {
185 // Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
186 // KDE::StatusBar::Sorry );
187 // return bundles;
188 // }
190 // for( BundleList::iterator it = playlist.bundles().begin();
191 // it != playlist.bundles().end();
192 // ++it )
193 // {
194 // bundles += MetaBundle( (*it).url() );
195 // }
196 // preparePlaylistForSync( name, bundles );
197 // return bundles;
200 BundleList
201 MediaDevice::bundlesToSync( const QString &name, const QString &query )
203 // const QStringList values = CollectionDB::instance()->query( query );
205 // BundleList bundles;
206 // for( QStringList::const_iterator it = values.begin(); it != values.end(); ++it )
207 // bundles += CollectionDB::instance()->bundleFromQuery( &it );
208 // preparePlaylistForSync( name, bundles );
209 // return bundles;
212 void
213 MediaDevice::preparePlaylistForSync( const QString &name, const BundleList &bundles )
215 if( ! m_playlistItem ) // might be syncing a new playlist from the playlist browser
216 return;
217 MediaItem *pl = m_playlistItem->findItem( name );
218 if( pl )
220 MediaItem *next = 0;
221 for( MediaItem *it = static_cast<MediaItem *>(pl->firstChild());
223 it = next )
225 next = static_cast<MediaItem *>(it->nextSibling());
226 const MetaBundle *bundle = (*it).bundle();
227 if( !bundle )
228 continue;
229 if( isOnOtherPlaylist( name, *bundle ) )
230 continue;
231 if( isInBundleList( bundles, *bundle ) )
232 continue;
233 deleteItemFromDevice( it );
235 deleteItemFromDevice( pl, None );
237 purgeEmptyItems();
240 bool
241 MediaDevice::bundleMatch( const MetaBundle &b1, const MetaBundle &b2 )
243 if( b1.track() != b2.track() )
244 return false;
245 if( b1.title() != b2.title() )
246 return false;
247 if( b1.album() != b2.album() )
248 return false;
249 if( b1.artist() != b2.artist() )
250 return false;
251 #if 0
252 if( b1.discNumber() != b2.discNumber() )
253 return false;
254 if( b1.composer() != b2.composer() )
255 return false;
256 #endif
258 return true;
261 bool
262 MediaDevice::isInBundleList( const BundleList &bundles, const MetaBundle &b )
264 for( BundleList::const_iterator it = bundles.begin();
265 it != bundles.end();
266 ++it )
268 if( bundleMatch( b, *it ) )
269 return true;
272 return false;
275 bool
276 MediaDevice::isOnOtherPlaylist( const QString &playlistToAvoid, const MetaBundle &bundle )
278 for( MediaItem *it = static_cast<MediaItem *>(m_playlistItem->firstChild());
280 it = static_cast<MediaItem *>(it->nextSibling()) )
282 if( it->text( 0 ) == playlistToAvoid )
283 continue;
284 if( isOnPlaylist( *it, bundle ) )
285 return true;
288 return false;
291 bool
292 MediaDevice::isOnPlaylist( const MediaItem &playlist, const MetaBundle &bundle )
294 for( MediaItem *it = static_cast<MediaItem *>(playlist.firstChild());
296 it = static_cast<MediaItem *>(it->nextSibling()) )
298 const MetaBundle *b = (*it).bundle();
299 if( !b )
300 continue;
301 if( bundleMatch( *b, bundle ) )
302 return true;
305 return false;
308 void
309 MediaDevice::copyTrackFromDevice( MediaItem *item )
311 debug() << "copyTrackFromDevice: not copying " << item->url() << ": not implemented";
314 QString
315 MediaDevice::replaceVariables( const QString &cmd )
317 QString result = cmd;
318 result.replace( "%d", deviceNode() );
319 result.replace( "%m", mountPoint() );
320 return result;
323 int MediaDevice::runPreConnectCommand()
325 if( m_preconnectcmd.isEmpty() )
326 return 0;
328 QString cmd = replaceVariables( m_preconnectcmd );
330 debug() << "running pre-connect command: [" << cmd << "]";
331 int e=sysCall(cmd);
332 debug() << "pre-connect: e=" << e;
333 return e;
336 int MediaDevice::runPostDisconnectCommand()
338 if( m_postdisconnectcmd.isEmpty() )
339 return 0;
341 QString cmd = replaceVariables( m_postdisconnectcmd );
342 debug() << "running post-disconnect command: [" << cmd << "]";
343 int e=sysCall(cmd);
344 debug() << "post-disconnect: e=" << e;
346 return e;
349 int MediaDevice::sysCall( const QString &command )
351 if ( sysProc->isRunning() ) return -1;
353 sysProc->clearArguments();
354 (*sysProc) << command;
355 if (!sysProc->start( K3Process::Block, K3Process::AllOutput ))
356 kFatal() << i18n("could not execute %1", command.toLocal8Bit().data());
358 return (sysProc->exitStatus());
361 void
362 MediaDevice::abortTransfer()
364 setCanceled( true );
365 cancelTransfer();
368 bool
369 MediaDevice::kioCopyTrack( const KUrl &src, const KUrl &dst )
371 m_wait = true;
373 KIO::FileCopyJob *job = KIO::file_copy( src, dst,
374 -1 /* permissions */,
375 KIO::HideProgressInfo );
376 connect( job, SIGNAL( result( KIO::Job * ) ),
377 this, SLOT( fileTransferred( KIO::Job * ) ) );
379 bool tryToRemove = false;
380 while ( m_wait )
382 if( isCanceled() )
384 job->kill( KJob::EmitResult );
385 tryToRemove = true;
386 m_wait = false;
388 else
390 usleep(10000);
391 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
395 if( !tryToRemove )
397 if(m_copyFailed)
399 tryToRemove = true;
400 Amarok::StatusBar::instance()->longMessage(
401 i18n( "Media Device: Copying %1 to %2 failed" )
402 .arg( src.prettyUrl(), dst.prettyUrl() ),
403 KDE::StatusBar::Error );
405 else
407 MetaBundle bundle2(dst);
408 if(!bundle2.isValidMedia() && bundle2.filesize()==MetaBundle::Undetermined)
410 tryToRemove = true;
411 // probably s.th. went wrong
412 Amarok::StatusBar::instance()->longMessage(
413 i18n( "Media Device: Reading tags from %1 failed", dst.prettyUrl() ),
414 KDE::StatusBar::Error );
419 if( tryToRemove )
421 QFile::remove( dst.path() );
422 return false;
425 return true;
428 void
429 MediaDevice::fileTransferred( KIO::Job *job ) //SLOT
431 if(job->error())
433 m_copyFailed = true;
434 debug() << "file transfer failed: " << job->errorText();
436 else
438 m_copyFailed = false;
441 m_wait = false;
444 bool
445 MediaDevice::connectDevice( bool silent )
447 if( !lockDevice( true ) )
448 return false;
450 runPreConnectCommand();
451 openDevice( silent );
453 if( isConnected()
454 && MediaBrowser::instance()->currentDevice() != this
455 && MediaBrowser::instance()->currentDevice()
456 && !MediaBrowser::instance()->currentDevice()->isConnected() )
458 MediaBrowser::instance()->activateDevice( this );
460 m_parent->updateStats();
461 m_parent->updateButtons();
463 if( !isConnected() )
465 unlockDevice();
466 return false;
469 if( m_syncStats )
471 syncStatsFromDevice( 0 );
472 Scrobbler::instance()->m_submitter->syncComplete();
475 // delete podcasts already played
476 if( m_autoDeletePodcasts && m_podcastItem )
478 QList<MediaItem*> list;
479 //NOTE we assume that currentItem is the main target
480 int numFiles = m_view->getSelectedLeaves( m_podcastItem, &list, MediaView::OnlyPlayed );
482 if(numFiles > 0)
484 m_parent->m_stats->setText( i18np( "1 track to be deleted", "%1 tracks to be deleted", numFiles ) );
486 setProgress( 0, numFiles );
488 int numDeleted = deleteItemFromDevice( m_podcastItem, true );
489 purgeEmptyItems();
490 if( numDeleted < 0 )
492 Amarok::StatusBar::instance()->longMessage(
493 i18n( "Failed to purge podcasts already played" ),
494 KDE::StatusBar::Sorry );
496 else if( numDeleted > 0 )
498 Amarok::StatusBar::instance()->shortMessage(
499 i18np( "Purged 1 podcasts already played",
500 "Purged %1 podcasts already played",
501 numDeleted ) );
504 synchronizeDevice();
506 QTimer::singleShot( 1500, m_parent->m_progressBox, SLOT(hide()) );
507 m_parent->queue()->computeSize();
508 m_parent->updateStats();
511 unlockDevice();
513 updateRootItems();
515 if( m_deferredDisconnect )
517 m_deferredDisconnect = false;
518 disconnectDevice( m_runDisconnectHook );
521 Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully connected" ) );
523 m_parent->updateDevices();
525 return true;
528 bool
529 MediaDevice::disconnectDevice( bool postDisconnectHook )
531 DEBUG_BLOCK
533 abortTransfer();
535 debug() << "disconnecting: hook=" << postDisconnectHook;
537 if( !lockDevice( true ) )
539 m_runDisconnectHook = postDisconnectHook;
540 m_deferredDisconnect = true;
541 debug() << "disconnecting: locked";
542 return false;
544 debug() << "disconnecting: ok";
546 if( m_syncStats )
548 syncStatsToDevice();
551 closeDevice();
552 unlockDevice();
554 m_parent->updateStats();
556 bool result = true;
557 if( postDisconnectHook && runPostDisconnectCommand() != 0 )
559 Amarok::StatusBar::instance()->longMessage(
560 i18n( "Post-disconnect command failed, before removing device, please make sure that it is safe to do so." ),
561 KDE::StatusBar::Information );
562 result = false;
564 else
565 Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully disconnected" ) );
567 m_parent->updateDevices();
569 return result;
572 void
573 MediaDevice::syncStatsFromDevice( MediaItem *root )
575 MediaItem *it = static_cast<MediaItem *>( m_view->firstChild() );
576 if( root )
578 it = static_cast<MediaItem *>( root->firstChild() );
581 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
583 for( ; it; it = static_cast<MediaItem *>( it->nextSibling() ) )
585 switch( it->type() )
587 case MediaItem::TRACK:
588 if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
590 const MetaBundle *bundle = it->bundle();
591 for( int i=0; i<it->recentlyPlayed(); i++ )
593 // submit to last.fm
594 if( bundle->length() > 30
595 && !bundle->artist().isEmpty() && bundle->artist() != i18n( "Unknown" )
596 && !bundle->title().isEmpty() && bundle->title() != i18n( "Unknown" ) )
598 // don't submit tracks shorter than 30 sec or w/o artist/title
599 debug() << "scrobbling " << bundle->artist() << " - " << bundle->title();
600 SubmitItem *sit = new SubmitItem( bundle->artist(), bundle->album(), bundle->title(), bundle->length(), false /* fake time */ );
601 Scrobbler::instance()->m_submitter->submitItem( sit );
604 // increase Amarok playcount
605 QString url = CollectionDB::instance()->getURL( *bundle );
606 if( !url.isEmpty() )
608 QDateTime t = it->playTime();
609 CollectionDB::instance()->addSongPercentage( url, 100, "mediadevice", t.isValid() ? &t : 0 );
610 debug() << "played " << url;
614 if( it->ratingChanged() )
616 // copy rating from media device to Amarok
617 QString url = CollectionDB::instance()->getURL( *bundle );
618 debug() << "rating changed " << url << ": " << it->rating()/10;
619 if( !url.isEmpty() )
621 CollectionDB::instance()->setSongRating( url, it->rating()/10 );
622 it->setRating( it->rating() ); // prevent setting it again next time
626 break;
627 case MediaItem::PODCASTITEM:
628 if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
630 const MetaBundle *bundle = it->bundle();
631 if( it->played() || it->recentlyPlayed() )
633 if( PodcastEpisodeBundle *peb = bundle->podcastBundle() )
635 debug() << "marking podcast episode as played: " << peb->url();
636 //PORT 2.0
637 // if( PlaylistBrowser::instance() )
638 // {
639 // PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
640 // if ( p )
641 // p->setListened();
642 // else
643 // debug() << "did not find podcast episode: " << peb->url() << " from " << peb->parent();
644 // }
648 break;
650 default:
651 syncStatsFromDevice( it );
652 break;
657 void
658 MediaDevice::syncStatsToDevice( MediaItem *root )
660 MediaItem *it = static_cast<MediaItem *>( m_view->firstChild() );
661 if( root )
663 it = static_cast<MediaItem *>( root->firstChild() );
666 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
668 for( ; it; it = static_cast<MediaItem *>( it->nextSibling() ) )
670 switch( it->type() )
672 case MediaItem::TRACK:
673 if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
675 const MetaBundle *bundle = it->bundle();
676 QString url = CollectionDB::instance()->getURL( *bundle );
677 it->syncStatsFromPath( url );
679 break;
681 case MediaItem::PODCASTITEM:
682 if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
684 const MetaBundle *bundle = it->bundle();
685 if( PodcastEpisodeBundle *peb = bundle->podcastBundle() )
687 // //PORT 2.0
688 // if( PlaylistBrowser::instance() )
689 // {
690 // PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
691 // if( p )
692 // it->setListened( !p->isNew() );
693 // }
696 break;
698 default:
699 syncStatsToDevice( it );
700 break;
705 void
706 MediaDevice::transferFiles()
708 if( !lockDevice( true ) )
710 return;
713 setCanceled( false );
715 m_transferring = true;
716 m_parent->transferAction()->setEnabled( false );
718 setProgress( 0, m_parent->m_queue->childCount() );
720 // ok, let's copy the stuff to the device
722 KUrl::List existing, unplayable;
723 unsigned transcodeFail = 0;
724 // iterate through items
725 MediaItem *next = static_cast<MediaItem *>(m_parent->m_queue->firstChild());
726 while( next )
728 MediaItem *transferredItem = next;
729 transferredItem->setFailed( false );
730 transferredItem->m_flags |= MediaItem::Transferring;
731 next = static_cast<MediaItem *>( transferredItem->nextSibling() );
733 if( transferredItem->device() )
735 transferredItem->device()->copyTrackFromDevice( transferredItem );
736 m_parent->m_queue->subtractItemFromSize( transferredItem, true );
737 delete transferredItem;
738 setProgress( progress() + 1 );
739 m_parent->m_queue->itemCountChanged();
740 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
741 continue;
744 BundleList bundles;
745 if( transferredItem->type() == MediaItem::PLAYLIST )
747 if( transferredItem->flags() & MediaItem::SmartPlaylist )
748 bundles = bundlesToSync( transferredItem->text( 0 ), transferredItem->data() );
749 else
750 bundles = bundlesToSync( transferredItem->text( 0 ), KUrl( transferredItem->data() ) );
752 else if( transferredItem->bundle() )
753 bundles += *transferredItem->bundle();
754 else
756 // this should not happen
757 debug() << "invalid item in transfer queue";
758 m_parent->m_queue->subtractItemFromSize( transferredItem, true );
759 delete transferredItem;
760 m_parent->m_queue->itemCountChanged();
761 continue;
764 if( bundles.count() > 1 )
765 setProgress( progress(), MediaBrowser::instance()->m_progress->maximum() + bundles.count() - 1 );
767 QString playlist = transferredItem->m_playlistName;
768 for( BundleList::const_iterator it = bundles.begin();
769 it != bundles.end();
770 ++it )
772 if( isCanceled() )
773 break;
775 const MetaBundle *bundle = &(*it);
777 bool transcoding = false;
778 MediaItem *item = trackExists( *bundle );
779 if( item && playlist.isEmpty() )
781 Amarok::StatusBar::instance()->shortMessage( i18n( "Track already on media device: %1" ).
782 arg( (*it).url().prettyUrl() ),
783 KDE::StatusBar::Sorry );
784 existing += (*it).url();
785 setProgress( progress() + 1 );
786 continue;
788 else if( !item ) // the item does not yet exist on the media device
790 if( m_transcode && ( !isPlayable( *bundle ) || m_transcodeAlways ) )
792 QString preferred = supportedFiletypes().isEmpty() ? "mp3" : supportedFiletypes().first();
793 debug() << "transcoding " << bundle->url() << " to " << preferred;
794 KUrl transcoded = MediaBrowser::instance()->transcode( bundle->url(), preferred );
795 if( isCanceled() )
796 break;
797 if( transcoded.isEmpty() )
799 debug() << "transcoding failed";
800 transcodeFail++;
802 else
804 transcoding = true;
805 MetaBundle *transcodedBundle = new MetaBundle( transcoded );
806 transcodedBundle->setArtist( bundle->artist() );
807 transcodedBundle->setTitle( bundle->title() );
808 transcodedBundle->setComposer( bundle->composer() );
809 transcodedBundle->setAlbum( bundle->album() );
810 transcodedBundle->setGenre( bundle->genre() );
811 transcodedBundle->setComment( bundle->comment() );
812 transcodedBundle->setYear( bundle->year() );
813 transcodedBundle->setDiscNumber( bundle->discNumber() );
814 transcodedBundle->setTrack( bundle->track() );
815 if( bundle->podcastBundle() )
817 transcodedBundle->setPodcastBundle( *bundle->podcastBundle() );
818 transcodedBundle->copyFrom( *bundle->podcastBundle() );
820 bundle = transcodedBundle;
824 if( !isPlayable( *bundle ) )
826 Amarok::StatusBar::instance()->shortMessage( i18n( "Track not playable on media device: %1", bundle->url().path() ),
827 KDE::StatusBar::Sorry );
828 unplayable += (*it).url();
829 transferredItem->setFailed();
830 if( transcoding )
832 delete bundle;
833 bundle = 0;
835 setProgress( progress() + 1 );
836 continue;
838 item = copyTrackToDevice( *bundle );
841 if( !item ) // copyTrackToDevice() failed
843 if( !isCanceled() )
845 Amarok::StatusBar::instance()->longMessage(
846 i18n( "Failed to copy track to media device: %1", bundle->url().path() ),
847 KDE::StatusBar::Sorry );
848 transferredItem->setFailed();
852 if( transcoding )
854 if( m_transcodeRemove )
855 QFile( bundle->url().path() ).remove();
857 delete bundle;
858 bundle = 0;
861 if( isCanceled() )
862 break;
864 if( !item )
866 setProgress( progress() + 1 );
867 continue;
870 item->syncStatsFromPath( (*it).url().path() );
872 if( m_playlistItem && !playlist.isEmpty() )
874 MediaItem *pl = m_playlistItem->findItem( playlist );
875 if( !pl )
877 QList<MediaItem*> items;
878 pl = newPlaylist( playlist, m_playlistItem, items );
880 if( pl )
882 QList<MediaItem*> items;
883 items.append( item );
884 addToPlaylist( pl, pl->lastChild(), items );
888 setProgress( progress() + 1 );
891 transferredItem->m_flags &= ~MediaItem::Transferring;
893 if( isCanceled() )
895 m_parent->updateStats();
896 break;
899 if( !(transferredItem->flags() & MediaItem::Failed) )
901 m_parent->m_queue->subtractItemFromSize( transferredItem, true );
902 delete transferredItem;
903 m_parent->m_queue->itemCountChanged();
905 m_parent->updateStats();
907 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
909 synchronizeDevice();
910 unlockDevice();
911 fileTransferFinished();
913 QString msg;
914 if( unplayable.count() > 0 )
916 msg = i18np( "One track not playable on media device",
917 "%1 tracks not playable on media device", unplayable.count() );
919 if( existing.count() > 0 )
921 if( msg.isEmpty() )
922 msg = i18np( "One track already on media device",
923 "%1 tracks already on media device", existing.count() );
924 else
925 msg += i18np( ", one track already on media device",
926 ", %1 tracks already on media device", existing.count() );
928 if( transcodeFail > 0 )
930 if( msg.isEmpty() )
931 msg = i18np( "One track was not transcoded",
932 "%1 tracks were not transcoded", transcodeFail );
933 else
934 msg += i18np( ", one track was not transcoded",
935 ", %1 tracks were not transcoded", transcodeFail );
937 const ScriptManager* const sm = ScriptManager::instance();
938 if( !sm->transcodeScriptRunning().isEmpty() )
939 msg += i18n( " (no transcode script running)" );
942 if( unplayable.count() + existing.count() > 0 )
944 QString longMsg = i18n( "The following tracks were not transferred: ");
945 for( KUrl::List::Iterator it = existing.begin();
946 it != existing.end();
947 it++ )
949 longMsg += "<br>" + (*it).prettyUrl();
951 for( KUrl::List::Iterator it = unplayable.begin();
952 it != unplayable.end();
953 it++ )
955 longMsg += "<br>" + (*it).prettyUrl();
957 Amarok::StatusBar::instance()->shortLongMessage( msg, longMsg, KDE::StatusBar::Sorry );
959 else if( !msg.isEmpty() )
961 Amarok::StatusBar::instance()->shortMessage( msg, KDE::StatusBar::Sorry );
964 m_parent->updateButtons();
965 m_parent->queue()->save( Amarok::saveLocation() + "transferlist.xml" );
966 m_transferring = false;
968 if( m_deferredDisconnect )
970 m_deferredDisconnect = false;
971 disconnectDevice( m_runDisconnectHook );
973 else if( m_scheduledDisconnect )
975 disconnectDevice( true );
977 m_scheduledDisconnect = false;
981 MediaDevice::progress() const
983 return m_parent->m_progress->value();
986 void
987 MediaDevice::setProgress( const int progress, const int total )
989 if( total != -1 )
990 m_parent->m_progress->setRange( 0, total );
991 m_parent->m_progress->setValue( progress );
992 m_parent->m_progressBox->show();
995 void
996 MediaDevice::fileTransferFinished() //SLOT
998 m_parent->updateStats();
999 m_parent->m_progressBox->hide();
1000 m_parent->transferAction()->setEnabled( isConnected() && m_parent->queue()->childCount() > 0 );
1001 m_wait = false;
1006 MediaDevice::deleteFromDevice(MediaItem *item, int flags )
1008 MediaItem* fi = item;
1009 int count = 0;
1011 if ( !(flags & Recursing) )
1013 if( !lockDevice( true ) )
1014 return 0;
1016 setCanceled( false );
1018 m_deleting = true;
1020 QList<MediaItem*> list;
1021 //NOTE we assume that currentItem is the main target
1022 int numFiles = m_view->getSelectedLeaves(item, &list, MediaView::OnlySelected | ((flags & OnlyPlayed) ? MediaView::OnlyPlayed : MediaView::None) );
1024 m_parent->m_stats->setText( i18np( "1 track to be deleted", "%1 tracks to be deleted", numFiles ) );
1025 if( numFiles > 0 && (flags & DeleteTrack) )
1027 int button = KMessageBox::warningContinueCancel( m_parent,
1028 i18np( "<p>You have selected 1 track to be <b>irreversibly</b> deleted.",
1029 "<p>You have selected %1 tracks to be <b>irreversibly</b> deleted.",
1030 numFiles
1032 QString(),
1033 KGuiItem(i18n("&Delete"),"edit-delete") );
1035 if ( button != KMessageBox::Continue )
1037 m_parent->queue()->computeSize();
1038 m_parent->updateStats();
1039 m_deleting = false;
1040 unlockDevice();
1041 return 0;
1044 if(!isTransferring())
1046 setProgress( 0, numFiles );
1050 // don't return if numFiles==0: playlist items might be to delete
1052 if( !fi )
1053 fi = static_cast<MediaItem*>(m_view->firstChild());
1056 while( fi )
1058 MediaItem *next = static_cast<MediaItem*>(fi->nextSibling());
1060 if( isCanceled() )
1062 break;
1065 if( !fi->isVisible() )
1067 fi = next;
1068 continue;
1071 if( fi->isSelected() )
1073 int ret = deleteItemFromDevice(fi, flags);
1074 if( ret >= 0 && count >= 0 )
1075 count += ret;
1076 else
1077 count = -1;
1079 else
1081 if( fi->childCount() )
1083 int ret = deleteFromDevice( static_cast<MediaItem*>(fi->firstChild()), flags | Recursing );
1084 if( ret >= 0 && count >= 0 )
1085 count += ret;
1086 else
1087 count = -1;
1090 m_parent->updateStats();
1092 fi = next;
1095 if(!(flags & Recursing))
1097 purgeEmptyItems();
1098 synchronizeDevice();
1099 m_deleting = false;
1100 unlockDevice();
1102 if(!isTransferring())
1104 QTimer::singleShot( 1500, m_parent->m_progressBox, SLOT(hide()) );
1107 if( m_deferredDisconnect )
1109 m_deferredDisconnect = false;
1110 disconnectDevice( m_runDisconnectHook );
1113 m_parent->queue()->computeSize();
1114 m_parent->updateStats();
1116 return count;
1119 void
1120 MediaDevice::purgeEmptyItems( MediaItem *root )
1122 MediaItem *it = 0;
1123 if( root )
1125 it = static_cast<MediaItem *>(root->firstChild());
1127 else
1129 it = static_cast<MediaItem *>(m_view->firstChild());
1132 MediaItem *next = 0;
1133 for( ; it; it=next )
1135 next = static_cast<MediaItem *>(it->nextSibling());
1136 purgeEmptyItems( it );
1137 if( it->childCount() == 0 &&
1138 (it->type() == MediaItem::ARTIST ||
1139 it->type() == MediaItem::ALBUM ||
1140 it->type() == MediaItem::PODCASTCHANNEL) )
1141 delete it;
1145 bool
1146 MediaDevice::isPlayable( const MetaBundle &bundle )
1148 if( supportedFiletypes().isEmpty() )
1149 return true;
1151 QString type = bundle.url().path().section( ".", -1 ).toLower();
1152 return supportedFiletypes().contains( type );
1155 bool
1156 MediaDevice::isPreferredFormat( const MetaBundle &bundle )
1158 if( supportedFiletypes().isEmpty() )
1159 return true;
1161 QString type = bundle.url().path().section( ".", -1 ).toLower();
1162 return ( type == supportedFiletypes().first() );
1165 #include "MediaDevice.moc"