/*************************************************************************** * copyright: (C) 2006, 2007 Ian Monroe * * (C) 2006 Seb Ruiz * * (C) 2007 Maximilian Kossick ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef AMAROK_DAAPCLIENT_CPP #define AMAROK_DAAPCLIENT_CPP #include "addhostbase.h" #include "collectiondb.h" #include "collectionbrowser.h" #include "daapreader/reader.h" #include "daapreader/authentication/contentfetcher.h" #include "daapclient.h" #include "daapserver.h" #include "debug.h" #include "mediabrowser.h" #include "playlist.h" #include "proxy.h" #include "statusbar/statusbar.h" #include "tagdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //loading icons #include #include #include #if DNSSD_SUPPORT #include #include #include #endif AMAROK_EXPORT_PLUGIN( DaapClient ) DaapClient::DaapClient() : MediaDevice() #if DNSSD_SUPPORT , m_browser( 0 ) #endif , m_connected( false ) , m_sharingServer( 0 ) , m_broadcastServerCheckBox( 0 ) , m_broadcastServer( false ) // to abide by "all ports closed" policy, we default to not broadcasting music { DEBUG_BLOCK setName( "daapclient" ); m_name = i18n( "Shared Music" ); m_hasMountPoint = false; m_autoDeletePodcasts = false; m_syncStats = false; m_transcode = false; m_transcodeAlways = false; m_transcodeRemove = false; m_configure = false; m_customButton = true; m_transfer = false; KToolBar *toolbar = MediaBrowser::instance()->getToolBar(); KToolBarButton *customButton = toolbar->getButton( MediaBrowser::CUSTOM ); customButton->setText( i18n("Add computer") ); toolbar = CollectionBrowser::instance()->getToolBar(); toolbar->setIconText( KToolBar::IconTextRight, false ); m_broadcastButton = new KToolBarButton( "connect_creating", 0, toolbar, "broadcast_button", i18n("Share My Music") ); m_broadcastButton->setToggle( true ); TQToolTip::add( customButton, i18n( "List music from a remote host" ) ); TQToolTip::add( m_broadcastButton, i18n( "If this button is checked, then your music will be exported to the network" ) ); connect( m_broadcastButton, TQT_SIGNAL( toggled(int) ), TQT_SLOT( broadcastButtonToggled() ) ); MediaBrowser::instance()->insertChild( this ); } DaapClient::~DaapClient() { #if DNSSD_SUPPORT delete m_browser; #endif } bool DaapClient::isConnected() { return m_connected; } bool DaapClient::getCapacity( KIO::filesize_t* /* total */, KIO::filesize_t* /* available */ ) { return false; } bool DaapClient::lockDevice(bool /*tryOnly = false*/ ) { return true; } void DaapClient::unlockDevice() { return; } bool DaapClient::openDevice(bool /* silent=false */) { DEBUG_BLOCK m_connected = true; #if DNSSD_SUPPORT if ( !m_browser ) { m_browser = new DNSSD::ServiceBrowser("_daap._tcp"); m_browser->setName("daapServiceBrowser"); connect( m_browser, TQT_SIGNAL( serviceAdded( DNSSD::RemoteService::Ptr ) ), this, TQT_SLOT( foundDaap ( DNSSD::RemoteService::Ptr ) ) ); connect( m_browser, TQT_SIGNAL( serviceRemoved( DNSSD::RemoteService::Ptr ) ), this, TQT_SLOT( serverOffline ( DNSSD::RemoteService::Ptr ) ) ); m_browser->startBrowse(); } #endif TQStringList sl = AmarokConfig::manuallyAddedServers(); foreach( sl ) { TQStringList current = TQStringList::split(":", (*it) ); TQString host = current.first(); TQ_UINT16 port = current.last().toInt(); TQString ip = resolve( host ); if( ip != "0" ) { newHost( host, host, ip, port ); } } if( m_broadcastServer ) m_sharingServer = new DaapServer( this, "DaapServer" ); return true; } bool DaapClient::closeDevice() { m_view->clear(); TQObjectList* readers = queryList( "Daap::Reader"); TQObject* itRead; for( itRead = readers->first(); itRead; itRead = readers->next() ) { static_cast(itRead)->logoutRequest(); delete m_servers[ itRead->name() ]; m_servers.remove( itRead->name() ); } m_connected = false; m_servers.clear(); #if DNSSD_SUPPORT m_serverItemMap.clear(); delete m_browser; m_browser = 0; #endif delete m_sharingServer; m_sharingServer = 0; return true; } KURL DaapClient::getProxyUrl( const KURL& url ) { DEBUG_BLOCK Daap::Proxy* daapProxy = new Daap::Proxy( url, this, "daapProxy" ); return daapProxy->proxyUrl(); } void DaapClient::synchronizeDevice() { return; } MediaItem* DaapClient::copyTrackToDevice(const MetaBundle& /* bundle */) { return 0; } MediaItem* DaapClient::trackExists( const MetaBundle& ) { return 0; } int DaapClient::deleteItemFromDevice( MediaItem* /*item*/, int /*flags*/ ) { return 0; } void DaapClient::rmbPressed( TQListViewItem* qitem, const TQPoint& point, int ) { DEBUG_BLOCK enum Actions { APPEND, LOAD, TQUEUE, INFO, CONNECT, REMOVE, DOWNLOAD }; MediaItem *item = dynamic_cast(qitem); ServerItem* sitem = dynamic_cast(qitem); if( !item ) return; KURL::List urls; KPopupMenu menu( m_view ); switch( item->type() ) { case MediaItem::DIRECTORY: menu.insertItem( SmallIconSet( "connect_creating" ), i18n( "&Connect" ), CONNECT ); if( sitem && !m_serverItemMap.tqcontains( sitem->key() ) ) { menu.insertItem( SmallIconSet( "remove" ), i18n("&Remove Computer"), REMOVE ); } { TQStringList sl = m_serverItemMap.keys(); foreach( sl ) { debug() << (*it) << endl; } debug() << sitem->key() << endl; } break; default: urls = m_view->nodeBuildDragList( 0 ); menu.insertItem( SmallIconSet( Amarok::icon( "playlist" ) ), i18n( "&Load" ), LOAD ); menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); menu.insertItem( SmallIconSet( Amarok::icon( "fastforward" ) ), i18n( "&Queue Tracks" ), TQUEUE ); menu.insertSeparator(); menu.insertItem( SmallIconSet( Amarok::icon( "playlist" ) ), i18n( "&Copy Files to Collection..." ), DOWNLOAD ); // albums and artists don't have bundles, so they crash... :( if( item->bundle() ) { menu.insertItem( SmallIconSet( Amarok::icon( "info" ) ), i18n( "Track &Information..." ), INFO ); } break; } int id = menu.exec( point ); switch( id ) { case CONNECT: if( ServerItem *s = dynamic_cast(item) ) { s->reset(); } item->setOpen( true ); break; case LOAD: Playlist::instance()->insertMedia( urls, Playlist::Replace ); break; case APPEND: Playlist::instance()->insertMedia( urls, Playlist::Append ); break; case TQUEUE: Playlist::instance()->insertMedia( urls, Playlist::Queue ); break; case INFO: { // The tag dialog automatically disables the widgets if the file is not local, which it is not. TagDialog *dialog = new TagDialog( *item->bundle(), 0 ); dialog->show(); } break; case REMOVE: if( sitem ) { TQStringList mas = AmarokConfig::manuallyAddedServers(); mas.remove( sitem->key() ); AmarokConfig::setManuallyAddedServers( mas ); delete sitem; } break; case DOWNLOAD: downloadSongs( urls ); break; } } void DaapClient::downloadSongs( KURL::List urls ) { DEBUG_BLOCK KURL::List realStreamUrls; KURL::List::Iterator it; for( it = urls.begin(); it != urls.end(); ++it ) realStreamUrls << Daap::Proxy::realStreamUrl( (*it), getSession( (*it).host() + ':' + TQString::number( (*it).port() ) ) ); ThreadManager::instance()->queueJob( new DaapDownloader( realStreamUrls ) ); } void DaapClient::serverOffline( DNSSD::RemoteService::Ptr service ) { #if DNSSD_SUPPORT DEBUG_BLOCK TQString key = serverKey( service.data() ); if( m_serverItemMap.tqcontains( key ) ) { ServerItem* removeMe = m_serverItemMap[ key ]; if( removeMe ) { delete removeMe; removeMe = 0; } else warning() << "root item already null" << endl; m_serverItemMap.remove( key ); } else warning() << "removing non-existant service" << endl; #endif } #if DNSSD_SUPPORT TQString DaapClient::serverKey( const DNSSD::RemoteService* service ) const { return ServerItem::key( service->hostName(), service->port() ); } #endif void DaapClient::foundDaap( DNSSD::RemoteService::Ptr service ) { #if DNSSD_SUPPORT DEBUG_BLOCK connect( service, TQT_SIGNAL( resolved( bool ) ), this, TQT_SLOT( resolvedDaap( bool ) ) ); service->resolveAsync(); #endif } void DaapClient::resolvedDaap( bool success ) { #if DNSSD_SUPPORT DEBUG_BLOCK const DNSSD::RemoteService* service = dynamic_cast(sender()); if( !success || !service ) return; debug() << service->serviceName() << ' ' << service->hostName() << ' ' << service->domain() << ' ' << service->type() << endl; TQString ip = resolve( service->hostName() ); if( ip == "0" || m_serverItemMap.tqcontains(serverKey( service )) ) //same server from multiple interfaces return; m_serverItemMap[ serverKey( service ) ] = newHost( service->serviceName(), service->hostName(), ip, service->port() ); #endif } void DaapClient::createTree( const TQString& /*host*/, Daap::SongList bundles ) { DEBUG_BLOCK const Daap::Reader* callback = dynamic_cast(sender()); if( !callback ) { debug() << "No callback!" << endl; return; } { const TQString hostKey = callback->name(); ServerInfo* si = new ServerInfo(); si->sessionId = callback->sessionId(); m_servers[ hostKey ] = si; } ServerItem* root = callback->rootMediaItem(); TQStringList artists = bundles.keys(); foreach( artists ) { MediaItem* tqparentArtist = new MediaItem( root ); tqparentArtist->setType( MediaItem::ARTIST ); Daap::AlbumList albumMap = *( bundles.tqfind(*it) ); tqparentArtist->setText( 0, (*albumMap.begin()).getFirst()->artist() ); //map was made case insensitively //just get the displayed-case from //the first track TQStringList albums = albumMap.keys(); for ( TQStringList::Iterator itAlbum = albums.begin(); itAlbum != albums.end(); ++itAlbum ) { MediaItem* tqparentAlbum = new MediaItem( tqparentArtist ); tqparentAlbum->setType( MediaItem::ALBUM ); MetaBundle* track; Daap::TrackList trackList = *albumMap.tqfind(*itAlbum); tqparentAlbum->setText( 0, trackList.getFirst()->album() ); for( track = trackList.first(); track; track = trackList.next() ) { if( m_removeDuplicates && trackExistsInCollection( track ) ) continue; MediaItem* childTrack = new MediaItem( tqparentAlbum ); childTrack->setText( 0, track->title() ); childTrack->setType( MediaItem::TRACK ); childTrack->setBundle( track ); childTrack->m_order = track->track(); } if( !tqparentAlbum->childCount() ) delete tqparentAlbum; } if( !tqparentArtist->childCount() ) delete tqparentArtist; } root->resetTitle(); root->stopAnimation(); root->setOpen( true ); } int DaapClient::incRevision( const TQString& host ) { if( m_servers.tqcontains(host) ) { m_servers[host]->revisionID++; return m_servers[host]->revisionID; } else return 0; } int DaapClient::getSession( const TQString& host ) { if( m_servers.tqcontains(host) ) return m_servers[host]->sessionId; else return -1; } void DaapClient::customClicked() { class AddHostDialog : public KDialogBase { public: AddHostDialog( TQWidget *tqparent ) : KDialogBase( tqparent, "DaapAddHostDialog", true, i18n( "Add Computer" ) , Ok|Cancel) { m_base = new AddHostBase( this, "DaapAddHostBase" ); m_base->m_downloadPixmap->setPixmap( TQPixmap( KGlobal::iconLoader()->iconPath( Amarok::icon( "download" ), -KIcon::SizeEnormous ) ) ); m_base->m_hostName->setFocus(); setMainWidget( m_base ); } AddHostBase* m_base; }; AddHostDialog dialog( 0 ); if( dialog.exec() == TQDialog::Accepted ) { TQString ip = resolve( dialog.m_base->m_hostName->text() ); if( ip == "0" ) Amarok::StatusBar::instance()->shortMessage( i18n("Could not resolve %1.").tqarg( dialog.m_base->m_hostName->text() ) ); else { TQString key = ServerItem::key( dialog.m_base->m_hostName->text(), dialog.m_base->m_portInput->value() ); if( !AmarokConfig::manuallyAddedServers().tqcontains( key ) ) { TQStringList mas = AmarokConfig::manuallyAddedServers(); mas.append( key ); AmarokConfig::setManuallyAddedServers( mas ); } newHost( dialog.m_base->m_hostName->text(), dialog.m_base->m_hostName->text(), ip, dialog.m_base->m_portInput->value() ); } } } ServerItem* DaapClient::newHost( const TQString& serviceName, const TQString& host, const TQString& ip, const TQ_INT16 port ) { if( ip.isEmpty() ) return 0; return new ServerItem( m_view, this, ip, port, serviceName, host ); } void DaapClient::passwordPrompt() { class PasswordDialog : public KDialogBase { public: PasswordDialog( TQWidget *tqparent ) : KDialogBase( tqparent, "PasswordDialog", true, i18n( "Password Required" ) , Ok|Cancel) { makeHBoxMainWidget(); KGuiItem ok( KStdGuiItem::ok() ); ok.setText( i18n( "Login" ) ); ok.setToolTip( i18n("Login to the music share with the password given.") ); setButtonOK( ok ); TQLabel* passIcon = new TQLabel( mainWidget(), "passicon" ); passIcon->setPixmap( TQPixmap( KGlobal::iconLoader()->iconPath( "password", -KIcon::SizeHuge ) ) ); TQHBox* loginArea = new TQHBox( mainWidget(), "passhbox" ); new TQLabel( i18n( "Password:"), loginArea, "passlabel" ); m_input = new KPasswordEdit( loginArea, "passedit" ); m_input->setFocus(); } KPasswordEdit* m_input; }; Daap::Reader* callback = dynamic_cast( const_cast( sender() ) ); if (!callback) { debug() << "No callback!" << endl; return; } ServerItem* root = callback->rootMediaItem(); PasswordDialog dialog( 0 ); if( dialog.exec() == TQDialog::Accepted ) { Daap::Reader* reader = new Daap::Reader( callback->host(), callback->port(), root, TQString( dialog.m_input->password() ), this, callback->name() ); root->setReader( reader ); connect( reader, TQT_SIGNAL( daapBundles( const TQString&, Daap::SongList ) ), this, TQT_SLOT( createTree( const TQString&, Daap::SongList ) ) ); connect( reader, TQT_SIGNAL( passwordRequired() ), this, TQT_SLOT( passwordPrompt() ) ); connect( reader, TQT_SIGNAL( httpError( const TQString& ) ), root, TQT_SLOT( httpError( const TQString& ) ) ); reader->loginRequest(); } else { root->setOpen( false ); root->resetTitle(); root->unLoaded(); } callback->deleteLater(); } TQString DaapClient::resolve( const TQString& hostname ) { KNetwork::KResolver resolver( hostname ); resolver.setFamily( KNetwork::KResolver::KnownFamily ); //A druidic incantation from Thiago. Works around a KResolver bug #132851 resolver.start(); if( resolver.wait( 5000 ) ) { KNetwork::KResolverResults results = resolver.results(); if( results.error() ) debug() << "Error resolving " << hostname << ": (" << resolver.errorString( results.error() ) << ")" << endl; if( !results.empty() ) { TQString ip = results[0].address().asInet().ipAddress().toString(); debug() << "ip found is " << ip << endl; return ip; } } return "0"; //error condition } const bool DaapClient::trackExistsInCollection( MetaBundle *bundle ) { /// FIXME slow. QueryBuilder qb; qb.addMatch( QueryBuilder::tabSong , QueryBuilder::valTitle, bundle->title() , true, false ); qb.addMatch( QueryBuilder::tabArtist, QueryBuilder::valName , bundle->artist(), true, false ); qb.addMatch( QueryBuilder::tabAlbum , QueryBuilder::valName , bundle->album() , true, false ); qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabSong, QueryBuilder::valURL ); TQStringList values = qb.run(); return ( values[0].toInt() > 0 ); } /// Configuration Dialog Extension void DaapClient::addConfigElements( TQWidget * tqparent ) { m_broadcastServerCheckBox = new TQCheckBox( "Broadcast my music", tqparent ); m_broadcastServerCheckBox->setChecked( m_broadcastServer ); m_removeDuplicatesCheckBox = new TQCheckBox( "Hide songs in my collection", tqparent ); m_removeDuplicatesCheckBox->setChecked( m_removeDuplicates ); TQToolTip::add( m_removeDuplicatesCheckBox, i18n( "Enabling this may reduce connection times" ) ); } void DaapClient::removeConfigElements( TQWidget * /* tqparent */ ) { if( m_broadcastServerCheckBox != 0 ) delete m_broadcastServerCheckBox; if( m_removeDuplicatesCheckBox != 0 ) delete m_removeDuplicatesCheckBox; m_broadcastServerCheckBox = 0; m_removeDuplicatesCheckBox = 0; } void DaapClient::loadConfig() { MediaDevice::loadConfig(); m_broadcastServer = configBool( "broadcastServer", false ); m_removeDuplicates = configBool( "removeDuplicates", false ); // don't undo all the work we just did at startup m_broadcastButton->blockSignals( true ); m_broadcastButton->setOn( m_broadcastServer ); m_broadcastButton->blockSignals( false ); } void DaapClient::applyConfig() { if( m_broadcastServerCheckBox ) m_broadcastServer = m_broadcastServerCheckBox->isChecked(); if( m_removeDuplicatesCheckBox ) m_removeDuplicates = m_removeDuplicatesCheckBox->isChecked(); setConfigBool( "broadcastServer" , m_broadcastServer ); setConfigBool( "removeDuplicates", m_removeDuplicates ); } void DaapClient::broadcastButtonToggled() { DEBUG_BLOCK m_broadcastServer = !m_broadcastServer; switch( m_broadcastServer ) { case false: debug() << "turning daap server off" << endl; if( m_sharingServer ) delete m_sharingServer; m_sharingServer = 0; break; case true: debug() << "turning daap server on" << endl; if( !m_sharingServer ) m_sharingServer = new DaapServer( this, "DaapServer" ); break; } } //////////////////////////////////////////////////////////////////////////////// // CLASS ServerItem //////////////////////////////////////////////////////////////////////////////// ServerItem::ServerItem( TQListView* tqparent, DaapClient* client, const TQString& ip, TQ_UINT16 port, const TQString& title, const TQString& host ) : MediaItem( tqparent ) , m_daapClient( client ) , m_reader( 0 ) , m_ip( ip ) , m_port( port ) , m_title( title ) , m_host( host ) , m_loaded( false ) , m_loading1( new TQPixmap( locate("data", "amarok/images/loading1.png" ) ) ) , m_loading2( new TQPixmap( locate("data", "amarok/images/loading2.png" ) ) ) { setText( 0, title ); setType( MediaItem::DIRECTORY ); } ServerItem::~ServerItem() { delete m_reader; m_reader = 0; } void ServerItem::reset() { delete m_reader; m_reader = 0; m_loaded = 0; TQListViewItem *c = firstChild(); TQListViewItem *n; while( c ) { n = c->nextSibling(); delete c; c = n; } } void ServerItem::setOpen( bool o ) { if( !o ) { MediaItem::setOpen( o ); return; } if( !m_loaded ) { //starts loading animation m_iconCounter = 1; startAnimation(); connect( &m_animationTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAnimation()) ); setText( 0, i18n( "Loading %1").tqarg( text( 0 ) ) ); Daap::Reader* reader = new Daap::Reader( m_ip, m_port, this, TQString(), m_daapClient, ( m_ip + ":3689" ).ascii() ); setReader ( reader ); connect( reader, TQT_SIGNAL( daapBundles( const TQString&, Daap::SongList ) ), m_daapClient, TQT_SLOT( createTree( const TQString&, Daap::SongList ) ) ); connect( reader, TQT_SIGNAL( passwordRequired() ), m_daapClient, TQT_SLOT( passwordPrompt() ) ); connect( reader, TQT_SIGNAL( httpError( const TQString& ) ), this, TQT_SLOT( httpError( const TQString& ) ) ); reader->loginRequest(); m_loaded = true; } else MediaItem::setOpen( true ); } void ServerItem::startAnimation() { if( !m_animationTimer.isActive() ) m_animationTimer.start( ANIMATION_INTERVAL ); } void ServerItem::stopAnimation() { m_animationTimer.stop(); setType( MediaItem::DIRECTORY ); //restore icon } void ServerItem::slotAnimation() { m_iconCounter % 2 ? setPixmap( 0, *m_loading1 ): setPixmap( 0, *m_loading2 ); m_iconCounter++; } void ServerItem::httpError( const TQString& errorString ) { stopAnimation(); resetTitle(); Amarok::StatusBar::instance()->longMessage( i18n( "The following error occurred while trying to connect to the remote server:
%1").tqarg( errorString ) ); m_reader->deleteLater(); m_reader = 0; m_loaded = false; } //////////////////////////////////////////////////////////////////////////////// // CLASS DaapDownloader //////////////////////////////////////////////////////////////////////////////// DaapDownloader::DaapDownloader( KURL::List urls ) : Job( "DaapDownloader" ) , m_urls( urls ) , m_ready( false ) , m_successful( false ) , m_errorOccured( false ) { // setDescription( i18n( "Downloading song from remote computer." ) ); //no new strings,uncomment after string freeze setDescription( i18n( "Downloading Media..." ) ); } bool DaapDownloader::doJob() { DEBUG_BLOCK KURL::List::iterator urlIt = m_urls.begin(); Daap::ContentFetcher* http = new Daap::ContentFetcher( (*urlIt).host(), (*urlIt).port(), TQString(), this ); connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( downloadFinished( int, bool ) ) ); connect( http, TQT_SIGNAL( dataReadProgress( int, int ) ), this, TQT_SLOT( dataReadProgress( int, int ) ) ); connect( http, TQT_SIGNAL( httpError( const TQString& ) ), this, TQT_SLOT( downloadFailed( const TQString& ) ) ); while( !isAborted() && !m_errorOccured && urlIt != m_urls.end() ) { m_ready = false; debug() << "downloading " << (*urlIt).path() << endl; setProgressTotalSteps( 100 ); KTempFile* tempNewFile = new KTempFile( TQString(), '.' + TQFileInfo( (*urlIt).path() ).extension() ); tempNewFile->setAutoDelete( true ); m_tempFileList.append( tempNewFile ); http->getDaap( (*urlIt).path() + (*urlIt).query(), tempNewFile->file() ); while( !m_ready && !isAborted() ) { msleep( 100 ); //Sleep 100 msec } debug() << "finished " << (*urlIt).path() << endl; ++urlIt; } debug() << "returning " << m_successful << endl; http->deleteLater(); http = 0; return m_successful; } void DaapDownloader::downloadFinished( int /*id*/, bool error ) { DEBUG_BLOCK m_tempFileList.last()->close(); setProgress100Percent(); //just to make sure m_successful = !error; m_ready = true; } void DaapDownloader::completeJob() { DEBUG_BLOCK KURL path; KURL::List tempUrlList; for( TQValueList::Iterator itTemps = m_tempFileList.begin(); itTemps != m_tempFileList.end(); ++itTemps ) { path.setPath( (*itTemps)->name() ); tempUrlList << path; } CollectionView::instance()->organizeFiles( tempUrlList, i18n( "Copy Files To Collection" ), false ); for( TQValueList::Iterator itTemps = m_tempFileList.begin(); itTemps != m_tempFileList.end(); ++itTemps ) delete (*itTemps); //autodelete is true, so file is unlinked now m_tempFileList.clear(); } void DaapDownloader::dataReadProgress( int done, int total ) { setProgress( int( ( float(done) / float(total) ) * 100.0 ) ); } void DaapDownloader::downloadFailed( const TQString & error ) { // Amarok::StatusBar::instance()->longMessageThreadSafe( i18n( "An error occured while downloading from remote music server." ), Amarok::StatusBar::Error ); DEBUG_BLOCK debug() << "failed on " << error << endl; m_successful = false; m_errorOccured = true; m_ready = true; } #include "daapclient.moc" #endif /* AMAROK_DAAPCLIENT_CPP */