diff --git a/src/EngineController.cpp b/src/EngineController.cpp index 76e00c7..5c1e7ec 100644 --- a/src/EngineController.cpp +++ b/src/EngineController.cpp @@ -22,6 +22,7 @@ #include "statusbar/StatusBar.h" #include "Debug.h" #include "MainWindow.h" +#include "MediaDeviceMonitor.h" #include "meta/Meta.h" #include "meta/MetaConstants.h" #include "meta/capabilities/MultiPlayableCapability.h" @@ -95,6 +96,7 @@ EngineController::initializePhonon() DEBUG_BLOCK delete m_media; + delete m_controller; delete m_audio; delete m_preamp; @@ -103,6 +105,8 @@ EngineController::initializePhonon() m_audio = new Phonon::AudioOutput( Phonon::MusicCategory, this ); m_path = Phonon::createPath( m_media, m_audio ); + + m_controller = new Phonon::MediaController( m_media ); // HACK we turn off replaygain manually on OSX, until the phonon coreaudio backend is fixed. // as the default is specified in the .cfg file, we can't just tell it to be a different default on OSX @@ -132,6 +136,8 @@ EngineController::initializePhonon() connect( m_media, SIGNAL( totalTimeChanged( qint64 ) ), SLOT( slotTrackLengthChanged( qint64 ) ) ); connect( m_media, SIGNAL( currentSourceChanged( const Phonon::MediaSource & ) ), SLOT( slotNewTrackPlaying( const Phonon::MediaSource & ) ) ); + connect( m_controller, SIGNAL( titleChanged( int ) ), SLOT( slotTitleChanged( int ) ) ); + //TODO: The xine engine does not support crossfading. Cannot get the gstreamer engine to work, will test this once I do. #if 0 @@ -318,7 +324,48 @@ EngineController::playUrl( const KUrl &url, uint offset ) slotStopFadeout(); debug() << "URL: " << url.url(); - m_media->setCurrentSource( url ); + + if ( url.url().startsWith( "audiocd:/" ) ) + { + //disconnect this signal for now or it will cause a loop that will cause a mutex lockup + disconnect( m_controller, SIGNAL( titleChanged( int ) ), this, SLOT( slotTitleChanged( int ) ) ); + + debug() << "play track from cd"; + QString trackNumberString = url.url(); + trackNumberString = trackNumberString.replace( "audiocd:/", QString() ); + + QStringList parts = trackNumberString.split( "/" ); + + if ( parts.count() != 2 ) + return; + + QString discId = parts.at( 0 ); + + //we really only want to play it if it is the disc that is currently present. + //In the case of cds for which we dont have any id, any "unknown" cds will + //be considdered equal. + + if ( MediaDeviceMonitor::instance()->currentCdId() != discId ) + return; + + + int trackNumber = parts.at( 1 ).toInt(); + + debug() << "3.2.1..."; + m_media->clear(); + m_media->setCurrentSource( Phonon::Cd ); + debug() << "boom?"; + m_controller->setCurrentTitle( trackNumber ); + debug() << "no boom?"; + + //reconnect it + connect( m_controller, SIGNAL( titleChanged( int ) ), SLOT( slotTitleChanged( int ) ) ); + + } + else + { + m_media->setCurrentSource( url ); + } m_nextTrack.clear(); m_nextUrl.clear(); @@ -524,7 +571,11 @@ EngineController::trackLength() const void EngineController::setNextTrack( Meta::TrackPtr track ) { + DEBUG_BLOCK + + debug() << "goin to lock mutex"; QMutexLocker locker( &m_mutex ); + debug() << "locked!"; if( !track ) return; @@ -629,6 +680,13 @@ EngineController::slotAboutToFinish() } } + else if ( m_currentTrack && m_currentTrack->playableUrl().url().startsWith( "audiocd:/" ) ) + { + debug() << "finished a cd track, dont care if queue is not empty, just get new track..."; + //m_media->stop(); + The::playlistActions()->requestNextTrack(); + slotQueueEnded(); + } else if( m_media->queue().isEmpty() ) The::playlistActions()->requestNextTrack(); } @@ -894,5 +952,14 @@ EngineController::slotStopFadeout() //SLOT } } +void EngineController::slotTitleChanged( int titleNumber ) +{ + DEBUG_BLOCK + Q_UNUSED( titleNumber ); + slotAboutToFinish(); +} + #include "EngineController.moc" + + diff --git a/src/EngineController.h b/src/EngineController.h index b65f656..8e98288 100644 --- a/src/EngineController.h +++ b/src/EngineController.h @@ -23,6 +23,7 @@ #include #include +#include #include //Needed for the slot class QTimer; @@ -134,6 +135,13 @@ private slots: void slotMetaDataChanged(); void slotStopFadeout(); //called after the fade-out has finished + /** + * Notify the engine that a new title has been reached when playing a cd. This + * is needed as a cd counts as basically one lone track, and we want to be able + * to play something else once one track has finished + */ + void slotTitleChanged( int titleNumber ); + private: static EngineController* s_instance; EngineController(); @@ -145,6 +153,7 @@ private: QPointer m_preamp; QPointer m_audio; QPointer m_fader; + QPointer m_controller; Phonon::Path m_path; Meta::TrackPtr m_currentTrack; diff --git a/src/MediaDeviceCache.cpp b/src/MediaDeviceCache.cpp index 4250c96..eed8f60 100644 --- a/src/MediaDeviceCache.cpp +++ b/src/MediaDeviceCache.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -80,8 +81,16 @@ MediaDeviceCache::refreshCache() { debug() << "Found Solid::DeviceInterface::StorageAccess with udi = " << device.udi(); debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); + Solid::StorageAccess* ssa = device.as(); - if( ssa ) + Solid::OpticalDisc * opt = device.as(); + + if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio ) + { + m_type[ device.udi() ] = MediaDeviceCache::SolidAudioCdType; + m_name[ device.udi() ] = device.vendor() + " - " + device.product(); + } + else if( ssa ) { if( !m_volumes.contains( device.udi() ) ) { @@ -121,12 +130,23 @@ MediaDeviceCache::slotAddSolidDevice( const QString &udi ) debug() << "Found new Solid device with udi = " << device.udi(); debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); Solid::StorageAccess *ssa = device.as(); + + Solid::OpticalDisc * opt = device.as(); + + if( m_type.contains( udi ) ) { debug() << "Duplicate UDI trying to be added: " << udi; return; } - if( device.as() ) + + if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio ) + { + debug() << "device is an Audio Cd"; + m_type[udi] = MediaDeviceCache::SolidAudioCdType; + m_name[udi] = device.vendor() + " - " + device.product(); + } + else if( device.as() ) { debug() << "Storage drive found, will wait for the volume"; return; diff --git a/src/MediaDeviceCache.h b/src/MediaDeviceCache.h index 279dba1..f10e615 100644 --- a/src/MediaDeviceCache.h +++ b/src/MediaDeviceCache.h @@ -35,7 +35,7 @@ class AMAROK_EXPORT MediaDeviceCache : public QObject public: - enum DeviceType { SolidPMPType, SolidVolumeType, ManualType, InvalidType }; + enum DeviceType { SolidPMPType, SolidVolumeType, ManualType, SolidAudioCdType, InvalidType }; static MediaDeviceCache* instance() { return s_instance ? s_instance : new MediaDeviceCache(); } diff --git a/src/MediaDeviceMonitor.cpp b/src/MediaDeviceMonitor.cpp index 9d8533b..1ecc45c 100644 --- a/src/MediaDeviceMonitor.cpp +++ b/src/MediaDeviceMonitor.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Alejandro Wainzinger + Copyright (C) 2009 Nikolaj Hald Nielsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -26,14 +27,19 @@ //solid specific includes #include + #include +#include #include #include #include +#include MediaDeviceMonitor* MediaDeviceMonitor::s_instance = 0; -MediaDeviceMonitor::MediaDeviceMonitor() : QObject() +MediaDeviceMonitor::MediaDeviceMonitor() + : QObject() + , m_currentCdId( QString() ) { DEBUG_BLOCK s_instance = this; @@ -77,6 +83,7 @@ MediaDeviceMonitor::checkDevices() checkDevicesForMtp(); checkDevicesForIpod(); + checkDevicesForCd(); } void @@ -117,6 +124,24 @@ MediaDeviceMonitor::checkDevicesForMtp() } } +void +MediaDeviceMonitor::checkDevicesForCd() +{ + DEBUG_BLOCK + + QStringList udiList = getDevices(); + + /* poll udi list for supported devices */ + foreach(const QString &udi, udiList ) + { + debug() << "udi: " << udi; + if ( isAudioCd( udi ) ) + { + emit audioCdDetected( udi ); + } + } +} + void @@ -201,6 +226,29 @@ MediaDeviceMonitor::isMtp( const QString &udi ) return pmp->supportedProtocols().contains( "mtp" ); } +bool +MediaDeviceMonitor::isAudioCd( const QString & udi ) +{ + DEBUG_BLOCK + + Solid::Device device; + + device = Solid::Device( udi ); + if( device.is() ) + { + debug() << "OpticalDisc"; + Solid::OpticalDisc * opt = device.as(); + if ( opt->availableContent() & Solid::OpticalDisc::Audio ) + { + debug() << "AudioCd"; + return true; + } + } + + return false; +} + + void MediaDeviceMonitor::connectIpod( const QString &mountpoint, const QString &udi ) { @@ -225,3 +273,64 @@ MediaDeviceMonitor::disconnectMtp( const QString &udi ) emit mtpReadyToDisconnect( udi ); } +QString +MediaDeviceMonitor::isCdPresent() +{ + DEBUG_BLOCK + + QStringList udiList = getDevices(); + + /* poll udi list for supported devices */ + foreach( const QString &udi, udiList ) + { + debug() << "udi: " << udi; + if ( isAudioCd( udi ) ) + { + return udi; + } + } + + return QString(); +} + +void +MediaDeviceMonitor::ejectCd( const QString & udi ) +{ + DEBUG_BLOCK + debug() << "trying to eject udi: " << udi; + Solid::Device device = Solid::Device( udi ).parent(); + + if ( !device.isValid() ) { + debug() << "invalid device, cannot eject"; + return; + } + + + debug() << "lets tryto get an OpticalDrive out of this thing"; + if( device.is() ) + { + debug() << "claims to be an OpticalDrive"; + Solid::OpticalDrive * drive = device.as(); + if ( drive ) + { + debug() << "ejecting the bugger"; + drive->eject(); + } + } +} + +QString +MediaDeviceMonitor::currentCdId() +{ + return m_currentCdId; +} + +void +MediaDeviceMonitor::setCurrentCdId( const QString & id ) +{ + m_currentCdId = id; +} + + + + diff --git a/src/MediaDeviceMonitor.h b/src/MediaDeviceMonitor.h index 1d1280e..a5a22b5 100644 --- a/src/MediaDeviceMonitor.h +++ b/src/MediaDeviceMonitor.h @@ -33,7 +33,7 @@ IpodCollectionFactory. #ifndef AMAROK_MEDIADEVICEMONITOR_H #define AMAROK_MEDIADEVICEMONITOR_H -#include "MediaDeviceInfo.h" +#include "collection/mediadevicecollection/support/MediaDeviceInfo.h" #include "amarok_export.h" @@ -59,6 +59,13 @@ class AMAROK_EXPORT MediaDeviceMonitor : public QObject void checkDevices(); // scans for supported devices void checkDevicesForMtp(); void checkDevicesForIpod(); + void checkDevicesForCd(); + + QString isCdPresent(); + void ejectCd( const QString &udi ); + + QString currentCdId(); + void setCurrentCdId( const QString &id ); // void fetchDevices(); // emits device info for each device present @@ -66,6 +73,7 @@ class AMAROK_EXPORT MediaDeviceMonitor : public QObject void deviceRemoved( const QString &udi ); void ipodDetected( const QString &mountPoint, const QString &udi ); void mtpDetected( const QString &serial, const QString &udi ); + void audioCdDetected( const QString &udi ); void ipodReadyToConnect( const QString &mountpoint, const QString &udi ); void ipodReadyToDisconnect( const QString &udi ); @@ -94,6 +102,9 @@ class AMAROK_EXPORT MediaDeviceMonitor : public QObject bool isIpod( const QString &udi ); bool isMtp( const QString &udi ); + bool isAudioCd( const QString &udi ); + + QString m_currentCdId; diff --git a/src/collection/CMakeLists.txt b/src/collection/CMakeLists.txt index 1b15485..70d9f14 100644 --- a/src/collection/CMakeLists.txt +++ b/src/collection/CMakeLists.txt @@ -3,6 +3,7 @@ include_directories( ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} ) set(AMAROK_COLLECTION_SUPPORT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/support) add_subdirectory( daap ) +add_subdirectory( audiocd ) add_subdirectory( mediadevicecollection ) add_subdirectory( ipodcollection ) diff --git a/src/collection/CollectionLocation.cpp b/src/collection/CollectionLocation.cpp index 18ffe84..b354f9b 100644 --- a/src/collection/CollectionLocation.cpp +++ b/src/collection/CollectionLocation.cpp @@ -176,11 +176,15 @@ CollectionLocation::abort() void CollectionLocation::getKIOCopyableUrls( const Meta::TrackList &tracks ) { + DEBUG_BLOCK QMap urls; foreach( Meta::TrackPtr track, tracks ) { if( track->isPlayable() ) + { urls.insert( track, track->playableUrl() ); + debug() << "adding url " << track->playableUrl(); + } } slotGetKIOCopyableUrlsDone( urls ); diff --git a/src/collection/audiocd/AudioCdCollection.cpp b/src/collection/audiocd/AudioCdCollection.cpp new file mode 100644 index 0000000..81b44d0 --- /dev/null +++ b/src/collection/audiocd/AudioCdCollection.cpp @@ -0,0 +1,585 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "AudioCdCollection.h" + +#include "AudioCdCollectionCapability.h" +#include "AudioCdCollectionLocation.h" +#include "AudioCdMeta.h" +#include "collection/CollectionManager.h" +#include "collection/support/MemoryQueryMaker.h" +#include "covermanager/CoverFetcher.h" +#include "Debug.h" +#include "MediaDeviceMonitor.h" +#include "MemoryQueryMaker.h" +#include "SvgHandler.h" + +#include +#include + +#include +#include + +#include + +AMAROK_EXPORT_PLUGIN( AudioCdCollectionFactory ) + +using namespace Meta; + +AudioCdCollectionFactory::AudioCdCollectionFactory() + : Amarok::CollectionFactory() + , m_collection( 0 ) + , m_currentUid( QString() ) +{ +} + +void +AudioCdCollectionFactory::init() +{ + DEBUG_BLOCK + connect( MediaDeviceMonitor::instance(), SIGNAL( audioCdDetected( const QString & ) ), this, SLOT( audioCdAdded( const QString & ) ) ); + connect( MediaDeviceMonitor::instance(), SIGNAL( deviceRemoved( const QString & ) ), this, SLOT( deviceRemoved( const QString & ) ) ); + + //check if there is a cd in the drive already: + + QString uid = MediaDeviceMonitor::instance()->isCdPresent(); + + if ( !uid.isEmpty() ) + { + m_currentUid = uid; + m_collection = new AudioCdCollection( uid ); + CollectionManager::instance()->addTrackProvider( m_collection ); + emit newCollection( m_collection ); + } + +} + +void +AudioCdCollectionFactory::audioCdAdded( const QString &uid ) +{ + DEBUG_BLOCK + if ( m_collection ) + { + delete m_collection; + m_collection = 0; + } + + m_currentUid = uid; + m_collection = new AudioCdCollection( uid ); + CollectionManager::instance()->addTrackProvider( m_collection ); + emit newCollection( m_collection ); +} + +void +AudioCdCollectionFactory::deviceRemoved( const QString &uid ) +{ + DEBUG_BLOCK + debug() << "uid: " << uid; + debug() << "m_currentUid: " << m_currentUid; + if ( m_currentUid == uid ) + { + CollectionManager::instance()->removeTrackProvider( m_collection ); + m_collection->cdRemoved(); //deleted by col. manager + m_collection = 0; + m_currentUid = QString(); + } +} + + +//////////////////////////// + +AudioCdCollection::AudioCdCollection( const QString &udi ) + : Collection() + , MemoryCollection() + , m_encodingFormat( OGG ) + , m_udi( udi ) +{ + DEBUG_BLOCK + + readAudioCdSettings(); + + m_ejectAction = new PopupDropperAction( The::svgHandler()->getRenderer( "amarok/images/pud_items.svg" ), + "eject", KIcon( "media-eject" ), i18n( "&Eject" ), 0 ); + + connect( m_ejectAction, SIGNAL( triggered() ), this, SLOT( eject() ) ); + + readCd(); +} + + +AudioCdCollection::~AudioCdCollection() +{ + MediaDeviceMonitor::instance()->setCurrentCdId( QString() ); + delete m_ejectAction; +} + +void +AudioCdCollection::readCd() +{ + DEBUG_BLOCK + + //get the CDDB info file if possible. + m_cdInfoJob = KIO::storedGet( KUrl( "audiocd:/Information/CDDB Information.txt" ), KIO::NoReload, KIO::HideProgressInfo ); + connect( m_cdInfoJob, SIGNAL( result( KJob * ) ) + , this, SLOT( infoFetchComplete( KJob *) ) ); + +} + +void +AudioCdCollection::infoFetchComplete( KJob *job ) +{ + DEBUG_BLOCK + if( job->error() ) + { + error() << job->error(); + m_cdInfoJob->deleteLater(); + noInfoAvailable(); + } + else + { + + QString cddbInfo = m_cdInfoJob->data(); + debug() << "got cddb info: " << cddbInfo; + + int startIndex; + int endIndex; + + QString artist; + QString album; + QString year; + QString genre; + + startIndex = cddbInfo.indexOf( "DTITLE=", 0 ); + if ( startIndex != -1 ) + { + startIndex += 7; + endIndex = cddbInfo.indexOf( "\n", startIndex ); + QString compoundTitle = cddbInfo.mid( startIndex, endIndex - startIndex ); + + debug() << "compoundTitle: " << compoundTitle; + + QStringList compoundTitleList = compoundTitle.split( " / " ); + + artist = compoundTitleList.at( 0 ); + album = compoundTitleList.at( 1 ); + } + + AudioCdArtistPtr artistPtr = AudioCdArtistPtr( new AudioCdArtist( artist ) ); + addArtist( ArtistPtr::staticCast( artistPtr ) ); + AudioCdAlbumPtr albumPtr = AudioCdAlbumPtr( new AudioCdAlbum( album ) ); + albumPtr->setAlbumArtist( artistPtr ); + addAlbum( AlbumPtr::staticCast( albumPtr ) ); + + + startIndex = cddbInfo.indexOf( "DYEAR=", 0 ); + if ( startIndex != -1 ) + { + startIndex += 6; + endIndex = cddbInfo.indexOf( "\n", startIndex ); + year = cddbInfo.mid( startIndex, endIndex - startIndex ); + } + + AudioCdYearPtr yearPtr = AudioCdYearPtr( new AudioCdYear( year ) ); + addYear( YearPtr::staticCast( yearPtr ) ); + + + startIndex = cddbInfo.indexOf( "DGENRE=", 0 ); + if ( startIndex != -1 ) + { + startIndex += 7; + endIndex = cddbInfo.indexOf( "\n", startIndex ); + genre = cddbInfo.mid( startIndex, endIndex - startIndex ); + } + + AudioCdGenrePtr genrePtr = AudioCdGenrePtr( new AudioCdGenre( genre ) ); + addGenre( GenrePtr::staticCast( genrePtr ) ); + + m_discCddbId = "unknown"; + + startIndex = cddbInfo.indexOf( "DISCID=", 0 ); + if ( startIndex != -1 ) + { + startIndex += 7; + endIndex = cddbInfo.indexOf( "\n", startIndex ); + m_discCddbId = cddbInfo.mid( startIndex, endIndex - startIndex ); + } + + MediaDeviceMonitor::instance()->setCurrentCdId( m_discCddbId ); + + //get the list of tracknames + startIndex = cddbInfo.indexOf( "TTITLE0=", 0 ); + if ( startIndex != -1 ) + { + endIndex = cddbInfo.indexOf( "\nEXTD=", startIndex ); + QString tracksBlock = cddbInfo.mid( startIndex, endIndex - startIndex ); + debug() << "Tracks block: " << tracksBlock; + QStringList tracksBlockList = tracksBlock.split( "\n" ); + + int numberOfTracks = tracksBlockList.count(); + + for ( int i = 0; i < numberOfTracks; i++ ) + { + QString prefix = "TTITLE" + QString::number( i ) + "="; + debug() << "prefix: " << prefix; + QString trackName = tracksBlockList.at( i ); + trackName = trackName.replace( prefix, "" ); + + QString trackArtist; + //check if a track artist is included in the track name: + + if ( trackName.contains( " / " ) ) + { + QStringList trackArtistList = trackName.split( " / " ); + trackName = trackArtistList.at( 1 ); + trackArtist = trackArtistList.at( 0 ); + + } + + debug() << "Track name: " << trackName; + + QString padding = i < 10 ? "0" : QString(); + + QString baseFileName = m_fileNamePattern; + debug() << "Track Base File Name (before): " << baseFileName; + + baseFileName.replace( "%{title}", trackName, Qt::CaseInsensitive ); + baseFileName.replace( "%{number}", padding + QString::number( i + 1 ), Qt::CaseInsensitive ); + baseFileName.replace( "%{albumtitle}", album, Qt::CaseInsensitive ); + baseFileName.replace( "%{trackartist}", trackArtist, Qt::CaseInsensitive ); + baseFileName.replace( "%{albumartist}", artist, Qt::CaseInsensitive ); + baseFileName.replace( "%{year}", year, Qt::CaseInsensitive ); + baseFileName.replace( "%{genre}", genre, Qt::CaseInsensitive ); + + //we hack the url so the engine controller knows what track on the cd to play.. + QString baseUrl = "audiocd:/" + m_discCddbId + "/" + QString::number( i + 1 ); + + debug() << "Track Base File Name (after): " << baseFileName; + debug() << "Track url: " << baseUrl; + + AudioCdTrackPtr trackPtr = AudioCdTrackPtr( new AudioCdTrack( this, trackName, baseUrl ) ); + + trackPtr->setTrackNumber( i + 1 ); + trackPtr->setFileNameBase( baseFileName ); + + addTrack( TrackPtr::staticCast( trackPtr ) ); + + artistPtr->addTrack( trackPtr ); + + if ( trackArtist.isEmpty() ) + trackPtr->setArtist( artistPtr ); + else + { + albumPtr->setIsCompilation( true ); + + AudioCdArtistPtr trackArtistPtr = AudioCdArtistPtr( new AudioCdArtist( trackArtist ) ); + trackArtistPtr->addTrack( trackPtr ); + trackPtr->setArtist( trackArtistPtr ); + } + + albumPtr->addTrack( trackPtr ); + trackPtr->setAlbum( albumPtr ); + + genrePtr->addTrack( trackPtr ); + trackPtr->setGenre( genrePtr ); + + yearPtr->addTrack( trackPtr ); + trackPtr->setYear( yearPtr ); + + } + } + + //lets see if we can find a cover for the album: + The::coverFetcher()->queueAlbum( AlbumPtr::staticCast( albumPtr ) ); + + } + + emit ( updated() ); + updateProxyTracks(); +} + +QueryMaker * +AudioCdCollection::queryMaker() +{ + return new MemoryQueryMaker( this, collectionId() ); +} + +QString +AudioCdCollection::collectionId() const +{ + return "AudioCd"; +} + +QString +AudioCdCollection::prettyName() const +{ + return "Audio Cd"; +} + +KIcon +AudioCdCollection::icon() const +{ + return KIcon( "media-optical-audio"); +} + +void +AudioCdCollection::cdRemoved() +{ + emit remove(); +} + +QString +AudioCdCollection::encodingFormat() const +{ + switch( m_encodingFormat ) { + case WAV: + return "vaw"; + case FLAC: + return "flac"; + case OGG: + return "ogg"; + case MP3: + return "mp3"; + } +} + +QString +AudioCdCollection::copyableBasePath() const +{ + switch( m_encodingFormat ) { + case WAV: + return "audiocd:/"; + case FLAC: + return "audiocd:/FLAC/"; + case OGG: + return "audiocd:/Ogg Vorbis/"; + case MP3: + return "audiocd:/MP3/"; + } +} + +void +AudioCdCollection::setEncodingFormat( int format ) const +{ + m_encodingFormat = format; +} + +CollectionLocation * +AudioCdCollection::location() const +{ + return new AudioCdCollectionLocation( this ); +} + +void +AudioCdCollection::eject() +{ + DEBUG_BLOCK + MediaDeviceMonitor::instance()->ejectCd( m_udi ); +} + +PopupDropperAction * +AudioCdCollection::ejectAction() +{ + return m_ejectAction; +} + +bool +AudioCdCollection::hasCapabilityInterface( Meta::Capability::Type type ) const +{ + return type == Meta::Capability::Collection; +} + +Meta::Capability * +AudioCdCollection::asCapabilityInterface( Meta::Capability::Type type ) +{ + if ( type == Meta::Capability::Collection ) + return new Meta::AudioCdCollectionCapability( this ); + else + return 0; +} + +void +AudioCdCollection::noInfoAvailable() +{ + + DEBUG_BLOCK + + m_discCddbId = "unknown"; + + MediaDeviceMonitor::instance()->setCurrentCdId( m_discCddbId ); + + QString artist = i18n( "Unknown" ); + QString album = i18n( "Unknown" ); + QString year = i18n( "Unknown" ); + QString genre = i18n( "Unknown" ); + + AudioCdArtistPtr artistPtr = AudioCdArtistPtr( new AudioCdArtist( artist ) ); + addArtist( ArtistPtr::staticCast( artistPtr ) ); + AudioCdAlbumPtr albumPtr = AudioCdAlbumPtr( new AudioCdAlbum( album ) ); + albumPtr->setAlbumArtist( artistPtr ); + addAlbum( AlbumPtr::staticCast( albumPtr ) ); + AudioCdYearPtr yearPtr = AudioCdYearPtr( new AudioCdYear( year ) ); + addYear( YearPtr::staticCast( yearPtr ) ); + AudioCdGenrePtr genrePtr = AudioCdGenrePtr( new AudioCdGenre( genre ) ); + addGenre( GenrePtr::staticCast( genrePtr ) ); + + + int i = 1; + QString prefix = i < 10 ? "0" : ""; + QString trackName = "Track " + prefix + QString::number( i ); + + while( KIO::NetAccess::exists( "audiocd:/" + trackName + ".wav", KIO::NetAccess::SourceSide,0 ) ) + { + + debug() << "got track: " << "audiocd:/" + trackName + ".wav"; + + QString baseUrl = "audiocd:/" + m_discCddbId + "/" + QString::number( i ); + + AudioCdTrackPtr trackPtr = AudioCdTrackPtr( new AudioCdTrack( this, trackName, baseUrl ) ); + + trackPtr->setTrackNumber( i ); + trackPtr->setFileNameBase( trackName ); + + addTrack( TrackPtr::staticCast( trackPtr ) ); + + artistPtr->addTrack( trackPtr ); + trackPtr->setArtist( artistPtr ); + + albumPtr->addTrack( trackPtr ); + trackPtr->setAlbum( albumPtr ); + + genrePtr->addTrack( trackPtr ); + trackPtr->setGenre( genrePtr ); + + yearPtr->addTrack( trackPtr ); + trackPtr->setYear( yearPtr ); + + i++; + prefix = i < 10 ? "0" : ""; + trackName = "Track " + prefix + QString::number( i ); + } + + emit ( updated() ); + updateProxyTracks(); + +} + +void +AudioCdCollection::readAudioCdSettings() +{ + KSharedConfigPtr conf = KSharedConfig::openConfig( "kcmaudiocdrc" ); + KConfigGroup filenameConf = conf->group( "FileName" ); + + m_fileNamePattern = filenameConf.readEntry( "file_name_template", "%{trackartist} - %{number} - %{title}" ); + m_albumNamePattern = filenameConf.readEntry( "album_name_template", "%{albumartist} - %{albumtitle}" ); +} + +bool +AudioCdCollection::possiblyContainsTrack(const KUrl & url) const +{ + DEBUG_BLOCK; + debug() << "match: " << url.url().startsWith( "audiocd:/" ); + + return url.url().startsWith( "audiocd:/" ); +} + +Meta::TrackPtr +AudioCdCollection::trackForUrl( const KUrl & url ) +{ + DEBUG_BLOCK; + + debug() << "Disk id: " << m_discCddbId; + + if ( !m_discCddbId.isEmpty() ) + { + + QString urlString = url.url().replace( "audiocd:/", "" ); + + QStringList parts = urlString.split( "/" ); + + if ( parts.count() != 2 ) + return TrackPtr(); + + QString discId = parts.at( 0 ); + + if ( discId != m_discCddbId ) + return TrackPtr(); + + int trackNumber = parts.at( 1 ).toInt(); + + foreach( TrackPtr track, trackMap().values() ) + { + if ( track->trackNumber() == trackNumber ) + return track; + } + + return TrackPtr(); + + } + else + { + if ( m_proxyMap.contains( url ) ) + { + return TrackPtr( m_proxyMap.value( url ) ); + } + else + { + MetaProxy::Track* ptrack = new MetaProxy::Track( url.url(), true ); + m_proxyMap.insert( url, ptrack ); + return TrackPtr( ptrack ); + } + } + +} + +void +AudioCdCollection::updateProxyTracks() +{ + foreach( KUrl url, m_proxyMap.keys() ) + { + + QString urlString = url.url().replace( "audiocd:/", "" ); + QStringList parts = urlString.split( "/" ); + + if ( parts.count() != 2 ) + continue; + + QString discId = parts.at( 0 ); + + if ( discId != m_discCddbId ) + continue; + + int trackNumber = parts.at( 1 ).toInt(); + + foreach( TrackPtr track, trackMap().values() ) + { + if ( track->trackNumber() == trackNumber ) + { + m_proxyMap.value( url )->updateTrack( track ); + } + } + + } + + m_proxyMap.clear(); +} + + + +#include "AudioCdCollection.moc" + diff --git a/src/collection/audiocd/AudioCdCollection.h b/src/collection/audiocd/AudioCdCollection.h new file mode 100644 index 0000000..e850dec --- /dev/null +++ b/src/collection/audiocd/AudioCdCollection.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AUDIOCDCOLLECTION_H +#define AUDIOCDCOLLECTION_H + +#include "Collection.h" +#include "context/popupdropper/libpud/PopupDropperAction.h" +#include "MemoryCollection.h" +#include "meta/proxy/MetaProxy.h" + +#include +#include + +#include + +class AudioCdCollection; + +class AudioCdCollectionFactory : public Amarok::CollectionFactory +{ + Q_OBJECT +public: + AudioCdCollectionFactory(); + virtual ~AudioCdCollectionFactory() {}; + + virtual void init(); + +private slots: + void audioCdAdded( const QString &uid ); + void deviceRemoved( const QString &uid ); + +private: + + QString m_currentUid; + AudioCdCollection * m_collection; + +}; + + +/** +This is a Memorycollection sublclass that uses the KIO audiocd:/ slave to populate itself whenever it detects a cd. + + @author Nikolaj Hald Nielsen +*/ +class AudioCdCollection : public Amarok::Collection, public MemoryCollection +{ + Q_OBJECT +public: + + enum { WAV, FLAC, OGG, MP3 } EncodingFormat; + + AudioCdCollection( const QString &udi ); + ~AudioCdCollection(); + + QString encodingFormat() const; + QString copyableBasePath() const; + + void setEncodingFormat( int format ) const; + + virtual QueryMaker * queryMaker(); + virtual QString collectionId() const; + virtual QString prettyName() const; + virtual KIcon icon() const; + + virtual CollectionLocation* location() const; + + bool possiblyContainsTrack( const KUrl &url ) const; + Meta::TrackPtr trackForUrl( const KUrl &url ); + + PopupDropperAction * ejectAction(); + + void cdRemoved(); + + virtual bool hasCapabilityInterface( Meta::Capability::Type type ) const; + virtual Meta::Capability* asCapabilityInterface( Meta::Capability::Type type ); + +public slots: + void infoFetchComplete( KJob *job ); + void eject(); + +private: + + void readAudioCdSettings(); + + /** + * Clear collection and read the cd currently in the drive, adding Artist, Album, + * Genre, Year and whatnot as detected by audiocd using CDDB. + */ + void readCd(); + + void noInfoAvailable(); + + void updateProxyTracks(); + + KIO::StoredTransferJob * m_cdInfoJob; + + QString m_cdName; + QString m_discCddbId; + QString m_udi; + mutable int m_encodingFormat; + + PopupDropperAction * m_ejectAction; + + QString m_fileNamePattern; + QString m_albumNamePattern; + + QMap m_proxyMap; + + + +}; + +#endif diff --git a/src/collection/audiocd/AudioCdCollectionCapability.cpp b/src/collection/audiocd/AudioCdCollectionCapability.cpp new file mode 100644 index 0000000..9207565 --- /dev/null +++ b/src/collection/audiocd/AudioCdCollectionCapability.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "AudioCdCollectionCapability.h" + +#include "AudioCdCollection.h" + +#include "context/popupdropper/libpud/PopupDropperAction.h" + +using namespace Meta; + +AudioCdCollectionCapability::AudioCdCollectionCapability( AudioCdCollection *collection ) + : CollectionCapability() + , m_collection( collection ) +{ +} + + +QList< PopupDropperAction * > Meta::AudioCdCollectionCapability::collectionActions(QueryMaker * qm) +{ + QList< PopupDropperAction* > actions; + actions.append( m_collection->ejectAction() ); + return actions; +} + +QList< PopupDropperAction * > Meta::AudioCdCollectionCapability::collectionActions(const TrackList tracklist) +{ + QList< PopupDropperAction* > actions; + actions.append( m_collection->ejectAction() ); + return actions; +} + + diff --git a/src/collection/audiocd/AudioCdCollectionCapability.h b/src/collection/audiocd/AudioCdCollectionCapability.h new file mode 100644 index 0000000..4f62068 --- /dev/null +++ b/src/collection/audiocd/AudioCdCollectionCapability.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AUDIOCDCOLLECTIONCAPABILITY_H +#define AUDIOCDCOLLECTIONCAPABILITY_H + +#include "Meta.h" + +#include "meta/capabilities/CollectionCapability.h" + +class AudioCdCollection; + +namespace Meta +{ + +/** + @author Nikolaj Hald Nielsen +*/ +class AudioCdCollectionCapability : public CollectionCapability { + Q_OBJECT + +public: + AudioCdCollectionCapability( AudioCdCollection *coll ); + + virtual QList collectionActions( QueryMaker *qm ); + virtual QList collectionActions( const TrackList tracklist ); + +private: + AudioCdCollection *m_collection; + + +}; + +} + +#endif diff --git a/src/collection/audiocd/AudioCdCollectionLocation.cpp b/src/collection/audiocd/AudioCdCollectionLocation.cpp new file mode 100644 index 0000000..3b9c140 --- /dev/null +++ b/src/collection/audiocd/AudioCdCollectionLocation.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "AudioCdCollectionLocation.h" + +#include "AudioCdMeta.h" +#include "Debug.h" +#include "FormatSelectionDialog.h" + +AudioCdCollectionLocation::AudioCdCollectionLocation( const AudioCdCollection* parentCollection ) + : CollectionLocation( parentCollection ) + , m_collection( parentCollection ) +{ +} + + +AudioCdCollectionLocation::~AudioCdCollectionLocation() +{ +} + +void AudioCdCollectionLocation::getKIOCopyableUrls( const Meta::TrackList & tracks ) +{ + DEBUG_BLOCK + + QMap resultMap; + foreach( Meta::TrackPtr trackPtr, tracks ) { + Meta::AudioCdTrackPtr cdTrack = Meta::AudioCdTrackPtr::staticCast( trackPtr ); + + QString path = m_collection->copyableBasePath() + cdTrack->fileNameBase() + '.' + m_collection->encodingFormat(); + + debug() << "adding path: " << path; + + resultMap.insert( trackPtr, KUrl( path ) ); + + } + + slotGetKIOCopyableUrlsDone( resultMap ); +} + +void AudioCdCollectionLocation::showSourceDialog(const Meta::TrackList & tracks, bool removeSources) +{ + DEBUG_BLOCK + FormatSelectionDialog * dlg = new FormatSelectionDialog(); + + connect( dlg, SIGNAL( formatSelected( int ) ), this, SLOT( onFormatSelected( int ) ) ); + connect( dlg, SIGNAL( rejected () ), this, SLOT( onCancel() ) ); + + dlg->show(); +} + +void AudioCdCollectionLocation::formatSelected(int format) +{ +} + +void AudioCdCollectionLocation::formatSelectionCancelled() +{ +} + +void AudioCdCollectionLocation::onFormatSelected( int format ) +{ + DEBUG_BLOCK + m_collection->setEncodingFormat( format ); + slotShowSourceDialogDone(); +} + +void AudioCdCollectionLocation::onCancel() +{ + DEBUG_BLOCK + abort(); +} + + +#include "AudioCdCollectionLocation.moc" + + diff --git a/src/collection/audiocd/AudioCdCollectionLocation.h b/src/collection/audiocd/AudioCdCollectionLocation.h new file mode 100644 index 0000000..b9dcaa6 --- /dev/null +++ b/src/collection/audiocd/AudioCdCollectionLocation.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AUDIOCDCOLLECTIONLOCATION_H +#define AUDIOCDCOLLECTIONLOCATION_H + +#include "CollectionLocation.h" + +#include "AudioCdCollection.h" + +/** +A custom CollectionLocation handling the encoding file type and so on for AudioCd collections + + @author Nikolaj Hald Nielsen +*/ +class AudioCdCollectionLocation : public CollectionLocation +{ + Q_OBJECT +public: + AudioCdCollectionLocation( const AudioCdCollection* parentCollection ); + ~AudioCdCollectionLocation(); + + virtual void getKIOCopyableUrls( const Meta::TrackList &tracks ); + + virtual void showSourceDialog( const Meta::TrackList &tracks, bool removeSources ); + +private slots: + + void formatSelected( int format ); + void formatSelectionCancelled(); + + void onFormatSelected( int format ); + void onCancel(); + +private: + + const AudioCdCollection * m_collection; + +}; + +#endif diff --git a/src/collection/audiocd/AudioCdMeta.cpp b/src/collection/audiocd/AudioCdMeta.cpp new file mode 100644 index 0000000..8112ed9 --- /dev/null +++ b/src/collection/audiocd/AudioCdMeta.cpp @@ -0,0 +1,593 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Maximilian Kossick + Copyright (C) 2009 Nikolaj Hald Nielsen + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "AudioCdMeta.h" +#include "AudioCdCollection.h" + +#include "Debug.h" + +using namespace Meta; + +AudioCdTrack::AudioCdTrack( AudioCdCollection *collection, const QString &name, const QString &url ) + : Meta::Track() + , m_collection( collection ) + , m_artist( 0 ) + , m_album( 0 ) + , m_genre( 0 ) + , m_composer( 0 ) + , m_year( 0 ) + , m_name( name) + , m_length( 0 ) + , m_trackNumber( 0 ) + , m_displayUrl( url ) + , m_playableUrl( url ) +{ +} + +AudioCdTrack::~AudioCdTrack() +{ + //nothing to do +} + +QString +AudioCdTrack::name() const +{ + return m_name; +} + +QString +AudioCdTrack::prettyName() const +{ + return m_name; +} + +KUrl +AudioCdTrack::playableUrl() const +{ + KUrl url( m_playableUrl ); + return url; +} + +QString +AudioCdTrack::uidUrl() const +{ + return m_playableUrl; +} + +QString +AudioCdTrack::prettyUrl() const +{ + return m_displayUrl; +} + +bool +AudioCdTrack::isPlayable() const +{ + return true; +} + +bool +AudioCdTrack::isEditable() const +{ + return false; +} + +AlbumPtr +AudioCdTrack::album() const +{ + return AlbumPtr::staticCast( m_album ); +} + +ArtistPtr +AudioCdTrack::artist() const +{ + return ArtistPtr::staticCast( m_artist ); +} + +GenrePtr +AudioCdTrack::genre() const +{ + return GenrePtr::staticCast( m_genre ); +} + +ComposerPtr +AudioCdTrack::composer() const +{ + return ComposerPtr::staticCast( m_composer ); +} + +YearPtr +AudioCdTrack::year() const +{ + return YearPtr::staticCast( m_year ); +} + +void +AudioCdTrack::setAlbum( const QString &newAlbum ) +{ + Q_UNUSED( newAlbum ) +} + +void +AudioCdTrack::setArtist( const QString &newArtist ) +{ + Q_UNUSED( newArtist ) +} + +void +AudioCdTrack::setComposer( const QString &newComposer ) +{ + Q_UNUSED( newComposer ) +} + +void +AudioCdTrack::setGenre( const QString &newGenre ) +{ + Q_UNUSED( newGenre ) +} + +void +AudioCdTrack::setYear( const QString &newYear ) +{ + Q_UNUSED( newYear ) +} + +QString +AudioCdTrack::comment() const +{ + return QString(); +} + +void +AudioCdTrack::setComment( const QString &newComment ) +{ + Q_UNUSED( newComment ) +} + +double +AudioCdTrack::score() const +{ + return 0.0; +} + +void +AudioCdTrack::setScore( double newScore ) +{ + Q_UNUSED( newScore ) +} + +int +AudioCdTrack::rating() const +{ + return 0; +} + +void +AudioCdTrack::setRating( int newRating ) +{ + Q_UNUSED( newRating ) +} + +int +AudioCdTrack::length() const +{ + return m_length; +} + +int +AudioCdTrack::filesize() const +{ + return 0; +} + +int +AudioCdTrack::sampleRate() const +{ + return 0; +} + +int +AudioCdTrack::bitrate() const +{ + return 0; +} + +int +AudioCdTrack::trackNumber() const +{ + return m_trackNumber; +} + +void +AudioCdTrack::setTrackNumber( int newTrackNumber ) +{ + m_trackNumber = newTrackNumber; +} + +int +AudioCdTrack::discNumber() const +{ + return 0; +} + +void +AudioCdTrack::setDiscNumber( int newDiscNumber ) +{ + Q_UNUSED( newDiscNumber ) +} + +int +AudioCdTrack::playCount() const +{ + return 0; +} + +uint +AudioCdTrack::lastPlayed() const +{ + return 0; +} + +QString +AudioCdTrack::type() const +{ + return m_collection->encodingFormat(); +} + +void +AudioCdTrack::subscribe( Observer *observer ) +{ + Q_UNUSED( observer ) //read only +} + +void +AudioCdTrack::unsubscribe( Observer *observer ) +{ + Q_UNUSED( observer ) //read only +} + +bool +AudioCdTrack::inCollection() const +{ + return true; +} + +Amarok::Collection* +AudioCdTrack::collection() const +{ + return m_collection; +} + +void +AudioCdTrack::setAlbum( AudioCdAlbumPtr album ) +{ + m_album = album; +} + +void +AudioCdTrack::setArtist( AudioCdArtistPtr artist ) +{ + m_artist = artist; +} + +void +AudioCdTrack::setGenre( AudioCdGenrePtr genre ) +{ + m_genre = genre; +} + +void +AudioCdTrack::setComposer( AudioCdComposerPtr composer ) +{ + m_composer = composer; +} + +void +AudioCdTrack::setYear( AudioCdYearPtr year ) +{ + m_year = year; +} + +void +AudioCdTrack::setTitle( const QString &title ) +{ + m_name = title; +} + +void +AudioCdTrack::setLength( int length ) +{ + m_length = length; +} + +void Meta::AudioCdTrack::setFileNameBase( const QString & fileNameBase ) +{ + m_fileNameBase = fileNameBase; +} + +QString Meta::AudioCdTrack::fileNameBase() +{ + return m_fileNameBase; +} + + +//AudioCdArtist + +AudioCdArtist::AudioCdArtist( const QString &name ) + : Meta::Artist() + , m_name( name ) + , m_tracks() +{ + //nothing to do +} + +AudioCdArtist::~AudioCdArtist() +{ + //nothing to do +} + +QString +AudioCdArtist::name() const +{ + return m_name; +} + +QString +AudioCdArtist::prettyName() const +{ + return m_name; +} + +TrackList +AudioCdArtist::tracks() +{ + return m_tracks; +} + +AlbumList +AudioCdArtist::albums() +{ + //TODO + return AlbumList(); +} + +void +AudioCdArtist::addTrack( AudioCdTrackPtr track ) +{ + m_tracks.append( TrackPtr::staticCast( track ) ); +} + +AudioCdAlbum::AudioCdAlbum( const QString &name ) + : Meta::Album() + , m_name( name ) + , m_tracks() + , m_isCompilation( false ) + , m_albumArtist( 0 ) +{ + //nothing to do +} + +AudioCdAlbum::~AudioCdAlbum() +{ + //nothing to do +} + +QString +AudioCdAlbum::name() const +{ + return m_name; +} + +QString +AudioCdAlbum::prettyName() const +{ + return m_name; +} + +bool +AudioCdAlbum::isCompilation() const +{ + DEBUG_BLOCK + return m_isCompilation; +} + +bool +AudioCdAlbum::hasAlbumArtist() const +{ + return !m_albumArtist.isNull(); +} + +ArtistPtr +AudioCdAlbum::albumArtist() const +{ + return ArtistPtr::staticCast( m_albumArtist ); +} + +TrackList +AudioCdAlbum::tracks() +{ + return m_tracks; +} + +QPixmap +AudioCdAlbum::image( int size ) +{ + if ( m_cover.isNull() ) + return Meta::Album::image( size ); + + //only cache during session + if ( m_coverSizeMap.contains( size ) ) + return m_coverSizeMap.value( size ); + + QPixmap scaled = m_cover.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + + m_coverSizeMap.insert( size, scaled ); + return scaled; +} + +bool +AudioCdAlbum::canUpdateImage() const +{ + return false; +} + +void +AudioCdAlbum::setImage( const QPixmap &pixmap ) +{ + m_cover = pixmap; +} + +void +AudioCdAlbum::addTrack( AudioCdTrackPtr track ) +{ + m_tracks.append( TrackPtr::staticCast( track ) ); +} + +void +AudioCdAlbum::setAlbumArtist( AudioCdArtistPtr artist ) +{ + m_albumArtist = artist; +} + +void +AudioCdAlbum::setIsCompilation( bool compilation ) +{ + DEBUG_BLOCK + m_isCompilation = compilation; +} + +//AudioCdGenre + +AudioCdGenre::AudioCdGenre( const QString &name ) + : Meta::Genre() + , m_name( name ) + , m_tracks() +{ + //nothing to do +} + +AudioCdGenre::~AudioCdGenre() +{ + //nothing to do +} + +QString +AudioCdGenre::name() const +{ + return m_name; +} + +QString +AudioCdGenre::prettyName() const +{ + return m_name; +} + +TrackList +AudioCdGenre::tracks() +{ + return m_tracks; +} + +void +AudioCdGenre::addTrack( AudioCdTrackPtr track ) +{ + m_tracks.append( TrackPtr::staticCast( track ) ); +} + +//AudioCdComposer + +AudioCdComposer::AudioCdComposer( const QString &name ) + : Meta::Composer() + , m_name( name ) + , m_tracks() +{ + //nothing to do +} + +AudioCdComposer::~AudioCdComposer() +{ + //nothing to do +} + +QString +AudioCdComposer::name() const +{ + return m_name; +} + +QString +AudioCdComposer::prettyName() const +{ + return m_name; +} + +TrackList +AudioCdComposer::tracks() +{ + return m_tracks; +} + +void +AudioCdComposer::addTrack( AudioCdTrackPtr track ) +{ + m_tracks.append( TrackPtr::staticCast( track ) ); +} + +//AudioCdYear + +AudioCdYear::AudioCdYear( const QString &name ) + : Meta::Year() + , m_name( name ) + , m_tracks() +{ + //nothing to do +} + +AudioCdYear::~AudioCdYear() +{ + //nothing to do +} + +QString +AudioCdYear::name() const +{ + return m_name; +} + +QString +AudioCdYear::prettyName() const +{ + return m_name; +} + +TrackList +AudioCdYear::tracks() +{ + return m_tracks; +} + +void +AudioCdYear::addTrack( AudioCdTrackPtr track ) +{ + m_tracks.append( TrackPtr::staticCast( track ) ); +} + + + diff --git a/src/collection/audiocd/AudioCdMeta.h b/src/collection/audiocd/AudioCdMeta.h new file mode 100644 index 0000000..7cbae55 --- /dev/null +++ b/src/collection/audiocd/AudioCdMeta.h @@ -0,0 +1,252 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Maximilian Kossick + Copyright (C) 2009 Nikolaj Hald Nielsen + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef AUDIOCDMETA_H +#define AUDIOCDMETA_H + +#include "Meta.h" + +class AudioCdCollection; + +namespace Meta +{ + +class AudioCdTrack; +class AudioCdAlbum; +class AudioCdArtist; +class AudioCdGenre; +class AudioCdComposer; +class AudioCdYear; + +typedef KSharedPtr AudioCdTrackPtr; +typedef KSharedPtr AudioCdArtistPtr; +typedef KSharedPtr AudioCdAlbumPtr; +typedef KSharedPtr AudioCdGenrePtr; +typedef KSharedPtr AudioCdComposerPtr; +typedef KSharedPtr AudioCdYearPtr; + +class AudioCdTrack : public Meta::Track +{ + public: + AudioCdTrack( AudioCdCollection *collection, const QString &name, const QString &url ); + virtual ~AudioCdTrack(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual KUrl playableUrl() const; + virtual QString uidUrl() const; + virtual QString prettyUrl() const; + + virtual bool isPlayable() const; + virtual bool isEditable() const; + + virtual AlbumPtr album() const; + virtual ArtistPtr artist() const; + virtual GenrePtr genre() const; + virtual ComposerPtr composer() const; + virtual YearPtr year() const; + + virtual void setAlbum ( const QString &newAlbum ); + virtual void setArtist ( const QString &newArtist ); + virtual void setGenre ( const QString &newGenre ); + virtual void setComposer ( const QString &newComposer ); + virtual void setYear ( const QString &newYear ); + + virtual void setTitle( const QString &newTitle ); + + virtual QString comment() const; + virtual void setComment ( const QString &newComment ); + + virtual double score() const; + virtual void setScore ( double newScore ); + + virtual int rating() const; + virtual void setRating ( int newRating ); + + virtual int length() const; + + virtual int filesize() const; + virtual int sampleRate() const; + virtual int bitrate() const; + + virtual int trackNumber() const; + virtual void setTrackNumber ( int newTrackNumber ); + + virtual int discNumber() const; + virtual void setDiscNumber ( int newDiscNumber ); + + virtual uint lastPlayed() const; + virtual int playCount() const; + + virtual QString type() const; + + virtual void beginMetaDataUpdate() {} //read only + virtual void endMetaDataUpdate() {} //read only + virtual void abortMetaDataUpdate() {} //read only + + virtual void subscribe ( Observer *observer ); + virtual void unsubscribe ( Observer *observer ); + + virtual bool inCollection() const; + virtual Amarok::Collection* collection() const; + + //AudioCdTrack specific methods + void setAlbum( AudioCdAlbumPtr album ); + void setArtist( AudioCdArtistPtr artist ); + void setComposer( AudioCdComposerPtr composer ); + void setGenre( AudioCdGenrePtr genre ); + void setYear( AudioCdYearPtr year ); + + void setLength( int length ); + + void setFileNameBase( const QString &fileNameBase ); + QString fileNameBase(); + + private: + AudioCdCollection *m_collection; + + AudioCdArtistPtr m_artist; + AudioCdAlbumPtr m_album; + AudioCdGenrePtr m_genre; + AudioCdComposerPtr m_composer; + AudioCdYearPtr m_year; + + QString m_name; + int m_length; + int m_trackNumber; + QString m_displayUrl; + QString m_playableUrl; + QString m_fileNameBase; +}; + +class AudioCdArtist : public Meta::Artist +{ + public: + AudioCdArtist( const QString &name ); + virtual ~AudioCdArtist(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual TrackList tracks(); + + virtual AlbumList albums(); + + //AudioCdArtist specific methods + void addTrack( AudioCdTrackPtr track ); + + private: + QString m_name; + TrackList m_tracks; +}; + +class AudioCdAlbum : public Meta::Album +{ + public: + AudioCdAlbum( const QString &name ); + virtual ~AudioCdAlbum(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual bool isCompilation() const; + virtual bool hasAlbumArtist() const; + virtual ArtistPtr albumArtist() const; + virtual TrackList tracks(); + + virtual QPixmap image( int size = 1 ); + virtual bool canUpdateImage() const; + virtual void setImage( const QPixmap &pixmap ); + + //AudioCdAlbum specific methods + void addTrack( AudioCdTrackPtr track ); + void setAlbumArtist( AudioCdArtistPtr artist ); + void setIsCompilation( bool compilation ); + + private: + QString m_name; + TrackList m_tracks; + bool m_isCompilation; + AudioCdArtistPtr m_albumArtist; + QPixmap m_cover; + QMap m_coverSizeMap; +}; + +class AudioCdGenre : public Meta::Genre +{ + public: + AudioCdGenre( const QString &name ); + virtual ~AudioCdGenre(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual TrackList tracks(); + + //AudioCdGenre specific methods + void addTrack( AudioCdTrackPtr track ); + + private: + QString m_name; + TrackList m_tracks; +}; + +class AudioCdComposer : public Meta::Composer +{ + public: + AudioCdComposer( const QString &name ); + virtual ~AudioCdComposer(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual TrackList tracks(); + + //AudioCdComposer specific methods + void addTrack( AudioCdTrackPtr track ); + + private: + QString m_name; + TrackList m_tracks; +}; + +class AudioCdYear : public Meta::Year +{ + public: + AudioCdYear( const QString &name ); + virtual ~AudioCdYear(); + + virtual QString name() const; + virtual QString prettyName() const; + + virtual TrackList tracks(); + + //AudioCdYear specific methods + void addTrack( AudioCdTrackPtr track ); + + private: + QString m_name; + TrackList m_tracks; +}; + +} + +#endif + diff --git a/src/collection/audiocd/CMakeLists.txt b/src/collection/audiocd/CMakeLists.txt new file mode 100644 index 0000000..119bfb8 --- /dev/null +++ b/src/collection/audiocd/CMakeLists.txt @@ -0,0 +1,36 @@ +include_directories( ../.. + ../../plugin + ../../meta + ../../collection + ${CMAKE_CURRENT_BINARY_DIR}/../.. + ${AMAROK_COLLECTION_SUPPORT_DIR} + ${KDE4_INCLUDE_DIR} + ${QT_INCLUDES} ) + + +########### next target ############### + + set(amarok_collection-audiocdcollection_PART_SRCS + AudioCdCollection.cpp + AudioCdMeta.cpp + AudioCdCollectionLocation.cpp + FormatSelectionDialog.cpp + AudioCdCollectionCapability.cpp) + + kde4_add_ui_files(amarok_collection-audiocdcollection_PART_SRCS FormatSelectionDialog.ui) + + + kde4_add_plugin(amarok_collection-audiocdcollection WITH_PREFIX ${amarok_collection-audiocdcollection_PART_SRCS}) + + target_link_libraries(amarok_collection-audiocdcollection amaroklib amarokpud ${KDE4_THREADWEAVER_LIBRARIES} ${KDE4_KDNSSD_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KUTILS_LIBS}) + + if(APPLE) + SET_TARGET_PROPERTIES(amarok_collection-audiocdcollection PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") + endif(APPLE) + + install(TARGETS amarok_collection-audiocdcollection DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES amarok_collection-audiocdcollection.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/src/collection/audiocd/FormatSelectionDialog.cpp b/src/collection/audiocd/FormatSelectionDialog.cpp new file mode 100644 index 0000000..d884580 --- /dev/null +++ b/src/collection/audiocd/FormatSelectionDialog.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "FormatSelectionDialog.h" + +#include "AudioCdCollection.h" + +#include + +FormatSelectionDialog::FormatSelectionDialog( QWidget *parent ) + : QDialog( parent ) +{ + setupUi( this ); + + connect( oggButton, SIGNAL( toggled( bool ) ), this, SLOT( selectionChanged( bool ) ) ); + connect( flacButton, SIGNAL( toggled( bool ) ), this, SLOT( selectionChanged( bool ) ) ); + connect( wavButton, SIGNAL( toggled( bool ) ), this, SLOT( selectionChanged( bool ) ) ); + connect( mp3Button, SIGNAL( toggled( bool ) ), this, SLOT( selectionChanged( bool ) ) ); + + connect( advancedButton, SIGNAL( clicked( bool ) ), this, SLOT( showAdvancedSettings() ) ); + + + //restore format from last time, if any. + KConfigGroup config = Amarok::config("Audio Cd Collection"); + QString format = config.readEntry( "Import Format", "ogg" ); + + if ( format.compare( "ogg", Qt::CaseInsensitive ) == 0 ) + oggButton->setChecked( true ); + else if ( format.compare( "flac", Qt::CaseInsensitive ) == 0 ) + flacButton->setChecked( true ); + else if ( format.compare( "wav", Qt::CaseInsensitive ) == 0 ) + wavButton->setChecked( true ); + else if ( format.compare( "mp3", Qt::CaseInsensitive ) == 0 ) + mp3Button->setChecked( true ); +} + + +FormatSelectionDialog::~FormatSelectionDialog() +{ +} + +void FormatSelectionDialog::selectionChanged( bool checked ) +{ + if ( !checked ) + return; + + if( sender() == oggButton ) + { + descriptionLabel->setText( i18n( "Ogg Vorbis is a fully free and unencumbered compressed audio format that is perfect for storing your compressed music on your computer. The sound quality is slightly better than Mp3 at the same bitrate. Note that not all mobile player support the Ogg Vorbis format" ) ); + + m_selectedFormat = AudioCdCollection::OGG; + } + else if( sender() == flacButton ) + { + descriptionLabel->setText( i18n( "Flac is a lossless compressed audio format free of any patents or licence fees. It maintains perfect CD audio quality while reducing fivawle size by about 50%. Because the filesize is much larger than Ogg Vorbis or Mp3 it is not reccomeded if you want to transfer your music to a mobile player." ) ); + + m_selectedFormat = AudioCdCollection::FLAC; + } + else if( sender() == wavButton ) + { + descriptionLabel->setText( i18n( "Wav is a basic, uncompressed audio file format. It takes up a lot of space but maintains perfect quality. It is generally not reccomended unless you know what you are doing. If you want perfect quailty, use Flac instead." ) ); + + m_selectedFormat = AudioCdCollection::WAV; + } + else if( sender() == mp3Button ) + { + descriptionLabel->setText( i18n( "Mp3 is the defacto standard in compressed audio compatible with almost all mobile players. It is however non free and genrally not reccomended" ) ); + + m_selectedFormat = AudioCdCollection::MP3; + } + +} + +void FormatSelectionDialog::accept() +{ + + //store to config for next download: + + QString format; + + if( m_selectedFormat == AudioCdCollection::OGG ) + format = "ogg"; + else if( m_selectedFormat == AudioCdCollection::FLAC ) + format = "flac"; + else if( m_selectedFormat == AudioCdCollection::WAV ) + format = "wav"; + else if( m_selectedFormat == AudioCdCollection::MP3 ) + format = "mp3"; + + KConfigGroup config = Amarok::config("Audio Cd Collection"); + config.writeEntry( "Import Format", format ); + + emit formatSelected( m_selectedFormat ); + QDialog::accept(); +} + +void FormatSelectionDialog::showAdvancedSettings() +{ + KCMultiDialog* KCM = new KCMultiDialog(); + KCM->setWindowTitle( i18n( "Audiocd settings - Amarok" ) ); + KCM->addModule( "audiocd" ); + KCM->exec(); + + KCM->deleteLater(); +} + + +#include "FormatSelectionDialog.moc" + diff --git a/src/collection/audiocd/FormatSelectionDialog.h b/src/collection/audiocd/FormatSelectionDialog.h new file mode 100644 index 0000000..bb3a8a6 --- /dev/null +++ b/src/collection/audiocd/FormatSelectionDialog.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2009 Nikolaj Hald Nielsen * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef FORMATSELECTIONDIALOG_H +#define FORMATSELECTIONDIALOG_H + +#include + +#include "ui_FormatSelectionDialog.h" + +/** +A dialog for selecting the format of of tracks imported from a AudioCdCollection + + @author +*/ +class FormatSelectionDialog + : public QDialog + , private Ui::FormatSelectionDialog +{ + Q_OBJECT +public: + FormatSelectionDialog( QWidget *parent = 0 ); + + ~FormatSelectionDialog(); + +public slots: + virtual void accept(); + + virtual void showAdvancedSettings(); + +signals: + void formatSelected( int ); + +private slots: + void selectionChanged( bool checked ); + +private: + int m_selectedFormat; + +}; + +#endif diff --git a/src/collection/audiocd/FormatSelectionDialog.ui b/src/collection/audiocd/FormatSelectionDialog.ui new file mode 100644 index 0000000..19664bc --- /dev/null +++ b/src/collection/audiocd/FormatSelectionDialog.ui @@ -0,0 +1,136 @@ + + + FormatSelectionDialog + + + + 0 + 0 + 484 + 347 + + + + Amarok + + + + + + + + + + 0 + 0 + + + + Available formats + + + + + + Ogg Vorbis + + + + + + + Flac + + + + + + + Wav + + + + + + + Mp3 + + + + + + + + + + Descripton + + + + + + + + + true + + + + + + + + + + Advanced + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + FormatSelectionDialog + accept() + + + 222 + 275 + + + 157 + 274 + + + + + buttonBox + rejected() + FormatSelectionDialog + reject() + + + 290 + 281 + + + 286 + 274 + + + + + diff --git a/src/collection/audiocd/amarok_collection-audiocdcollection.desktop b/src/collection/audiocd/amarok_collection-audiocdcollection.desktop new file mode 100644 index 0000000..1d47d88 --- /dev/null +++ b/src/collection/audiocd/amarok_collection-audiocdcollection.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Type=Service +Name=AudioCd Collection +Comment=AudioCd collection plugin for Amarok + +ServiceTypes=Amarok/Plugin + +X-KDE-Library=libamarok_collection-audiocdcollection + +X-KDE-Amarok-authors=Nikolaj Hald Nielsen +X-KDE-Amarok-email=anhnFreespirit@gmail.com +X-KDE-Amarok-framework-version=40 +X-KDE-Amarok-name=audiocd-collection +X-KDE-Amarok-plugintype=collection +X-KDE-Amarok-rank=100 +X-KDE-Amarok-version=1 diff --git a/src/collection/sqlcollection/SqlCollectionLocation.cpp b/src/collection/sqlcollection/SqlCollectionLocation.cpp index b3359d9..2244f14 100644 --- a/src/collection/sqlcollection/SqlCollectionLocation.cpp +++ b/src/collection/sqlcollection/SqlCollectionLocation.cpp @@ -173,73 +173,25 @@ SqlCollectionLocation::slotJobFinished( KJob *job ) } m_jobs.remove( job ); job->deleteLater(); - if( m_jobs.isEmpty() ) + + if( !startNextJob() ) { insertTracks( m_destinations ); insertStatistics( m_destinations ); m_collection->scanManager()->setBlockScan( false ); slotCopyOperationFinished(); } + } void SqlCollectionLocation::copyUrlsToCollection( const QMap &sources ) { m_collection->scanManager()->setBlockScan( true ); //make sure the collection scanner does not run while we are coyping stuff - bool jobsCreated = false; - foreach( const Meta::TrackPtr &track, sources.keys() ) - { - KIO::FileCopyJob *job = 0; - KUrl dest = m_destinations[ track ]; - dest.cleanPath(); - KUrl src = sources[ track ]; - src.cleanPath(); - debug() << "copying from " << src << " to " << dest; - KIO::JobFlags flags = KIO::HideProgressInfo; - if( m_overwriteFiles ) - { - flags |= KIO::Overwrite; - } - QFileInfo info( dest.pathOrUrl() ); - QDir dir = info.dir(); - if( !dir.exists() ) - { - if( !dir.mkpath( "." ) ) - { - warning() << "Could not create directory " << dir; - continue; - } - } - if( src == dest) { - //no changes, so leave the database alone, and don't erase anything - source()->setMovedByDestination( track, false ); - continue; - } - //we should only move it directly if we're moving within the same collection - else if( isGoingToRemoveSources() && source()->collection() == collection() ) - { - job = KIO::file_move( src, dest, -1, flags ); - source()->setMovedByDestination( track, true ); //remove old location from tracks table - } - else - { - //later on in the case that remove is called, the file will be deleted because we didn't apply moveByDestination to the track - job = KIO::file_copy( src, dest, -1, flags ); - } - if( job ) //just to be safe - { - connect( job, SIGNAL( result(KJob*) ), SLOT( slotJobFinished(KJob*) ) ); - QString name = track->prettyName(); - if( track->artist() ) - name = QString( "%1 - %2" ).arg( track->artist()->name(), track->prettyName() ); - The::statusBar()->newProgressOperation( job, i18n( "Transferring: %1", name ) ); - m_jobs.insert( job, track ); - job->start(); - jobsCreated = true; - } - } - if( !jobsCreated ) //this signal needs to be called no matter what, even if there are no job finishes to call it + m_sources = sources; + + if( !startNextJob() ) //this signal needs to be called no matter what, even if there are no job finishes to call it slotCopyOperationFinished(); } @@ -343,4 +295,65 @@ SqlCollectionLocation::insertStatistics( const QMap &tr } } +bool SqlCollectionLocation::startNextJob() +{ + DEBUG_BLOCK + if ( !m_sources.isEmpty() ) + { + Meta::TrackPtr track = m_sources.keys().first(); + KUrl src = m_sources.take( track ); + + bool jobsCreated = false; + KIO::FileCopyJob *job = 0; + KUrl dest = m_destinations[ track ]; + dest.cleanPath(); + + src.cleanPath(); + debug() << "copying from " << src << " to " << dest; + KIO::JobFlags flags = KIO::HideProgressInfo; + if( m_overwriteFiles ) + { + flags |= KIO::Overwrite; + } + QFileInfo info( dest.pathOrUrl() ); + QDir dir = info.dir(); + if( !dir.exists() ) + { + if( !dir.mkpath( "." ) ) + { + warning() << "Could not create directory " << dir; + return false; + } + } + if( src == dest) { + //no changes, so leave the database alone, and don't erase anything + source()->setMovedByDestination( track, false ); + return false; + } + //we should only move it directly if we're moving within the same collection + else if( isGoingToRemoveSources() && source()->collection() == collection() ) + { + job = KIO::file_move( src, dest, -1, flags ); + source()->setMovedByDestination( track, true ); //remove old location from tracks table + } + else + { + //later on in the case that remove is called, the file will be deleted because we didn't apply moveByDestination to the track + job = KIO::file_copy( src, dest, -1, flags ); + } + if( job ) //just to be safe + { + connect( job, SIGNAL( result(KJob*) ), SLOT( slotJobFinished(KJob*) ) ); + QString name = track->prettyName(); + if( track->artist() ) + name = QString( "%1 - %2" ).arg( track->artist()->name(), track->prettyName() ); + + The::statusBar()->newProgressOperation( job, i18n( "Transferring: %1", name ) ); + m_jobs.insert( job, track ); + return true; + } + } + return false; +} + #include "SqlCollectionLocation.moc" diff --git a/src/collection/sqlcollection/SqlCollectionLocation.h b/src/collection/sqlcollection/SqlCollectionLocation.h index c9da4fe..e478463 100644 --- a/src/collection/sqlcollection/SqlCollectionLocation.h +++ b/src/collection/sqlcollection/SqlCollectionLocation.h @@ -54,10 +54,13 @@ class SqlCollectionLocation : public CollectionLocation void slotJobFinished( KJob *job ); private: + bool startNextJob(); + QMap updatedMtime( const QStringList &urls ); SqlCollection *m_collection; QMap m_destinations; + QMap m_sources; bool m_overwriteFiles; QMap m_jobs; }; diff --git a/src/collection/support/MemoryQueryMaker.cpp b/src/collection/support/MemoryQueryMaker.cpp index b19c526..7417428 100644 --- a/src/collection/support/MemoryQueryMaker.cpp +++ b/src/collection/support/MemoryQueryMaker.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2007-2009 Maximilian Kossick - (c) 2007 Nikolaj Hald Nielsen + (c) 2007-2009 Nikolaj Hald Nielsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -74,6 +74,7 @@ struct MemoryQueryMaker::Private { qint64 orderByField; bool orderDescending; bool orderByNumberField; + AlbumQueryMode albumQueryMode; }; MemoryQueryMaker::MemoryQueryMaker( MemoryCollection *mc, const QString &collectionId ) @@ -115,6 +116,7 @@ MemoryQueryMaker::reset() d->orderByField = 0; d->orderDescending = false; d->orderByNumberField = false; + d->albumQueryMode = AllAlbums; return this; } @@ -359,7 +361,17 @@ MemoryQueryMaker::handleResult() } case QueryMaker::Track : { - TrackList tracks = m_collection->trackMap().values(); + TrackList tracks; + + foreach( TrackPtr track, m_collection->trackMap().values() ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + tracks.append( track ); + } + } if( d->orderByField ) { @@ -374,7 +386,19 @@ MemoryQueryMaker::handleResult() } case QueryMaker::Album : { - AlbumList albums = m_collection->albumMap().values(); + + AlbumList albums; + foreach( AlbumPtr album, m_collection->albumMap().values() ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && album->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !album->isCompilation()) ) + { + albums.append( album ); + break; + } + } + albums = orderListByName( albums, Meta::valAlbum ); emitProperResult( albums ); @@ -382,15 +406,44 @@ MemoryQueryMaker::handleResult() } case QueryMaker::Artist : { - ArtistList artists = m_collection->artistMap().values(); - artists = orderListByName( artists, Meta::valArtist ); + ArtistList artists; + foreach( ArtistPtr artist, m_collection->artistMap().values() ) + { + TrackList tracks = artist->tracks(); + foreach( TrackPtr track, tracks ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + artists.append( artist ); + break; + } + } + } + artists = orderListByName( artists, Meta::valArtist ); emitProperResult( artists ); break; } case QueryMaker::Composer : { - ComposerList composers = m_collection->composerMap().values(); + ComposerList composers; + foreach( ComposerPtr composer, m_collection->composerMap().values() ) + { + TrackList tracks = composer->tracks(); + foreach( TrackPtr track, tracks ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + composers.append( composer ); + break; + } + } + } + composers = orderListByName( composers, Meta::valComposer ); emitProperResult( composers ); @@ -398,7 +451,22 @@ MemoryQueryMaker::handleResult() } case QueryMaker::Genre : { - GenreList genres = m_collection->genreMap().values(); + GenreList genres; + foreach( GenrePtr genre, m_collection->genreMap().values() ) + { + TrackList tracks = genre->tracks(); + foreach( TrackPtr track, tracks ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + genres.append( genre ); + break; + } + } + } + genres = orderListByName( genres, Meta::valGenre ); emitProperResult( genres ); @@ -406,7 +474,21 @@ MemoryQueryMaker::handleResult() } case QueryMaker::Year : { - YearList years = m_collection->yearMap().values(); + YearList years; + foreach( YearPtr year, m_collection->yearMap().values() ) + { + TrackList tracks = year->tracks(); + foreach( TrackPtr track, tracks ) + { + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + years.append( year ); + break; + } + } + } //this a special case which requires a bit of code duplication //years have to be ordered as numbers, bu orderListByNumber does not work for Meta::YearPtrs @@ -511,7 +593,12 @@ MemoryQueryMaker::handleResult( const TrackList &tracks ) QSet albumSet; foreach( TrackPtr track, tracks ) { - albumSet.insert( track->album() ); + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + albumSet.insert( track->album() ); + } } AlbumList albumList = albumSet.toList(); albumList = orderListByName( albumList, Meta::valAlbum ); @@ -523,7 +610,12 @@ MemoryQueryMaker::handleResult( const TrackList &tracks ) QSet artistSet; foreach( TrackPtr track, tracks ) { - artistSet.insert( track->artist() ); + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + artistSet.insert( track->artist() ); + } } ArtistList list = artistSet.toList(); list = orderListByName( list, Meta::valArtist ); @@ -535,7 +627,12 @@ MemoryQueryMaker::handleResult( const TrackList &tracks ) QSet genreSet; foreach( TrackPtr track, tracks ) { - genreSet.insert( track->genre() ); + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + genreSet.insert( track->genre() ); + } } GenreList list = genreSet.toList(); list = orderListByName( list, Meta::valGenre ); @@ -547,7 +644,12 @@ MemoryQueryMaker::handleResult( const TrackList &tracks ) QSet composerSet; foreach( TrackPtr track, tracks ) { - composerSet.insert( track->composer() ); + if( d->albumQueryMode == AllAlbums + || ( d->albumQueryMode == OnlyCompilations && track->album()->isCompilation() ) + || ( d->albumQueryMode == OnlyNormalAlbums && !track->album()->isCompilation()) ) + { + composerSet.insert( track->composer() ); + } } ComposerList list = composerSet.toList(); list = orderListByName( list, Meta::valComposer ); @@ -905,4 +1007,9 @@ MemoryQueryMaker::done( ThreadWeaver::Job *job ) emit queryDone(); } +QueryMaker * MemoryQueryMaker::setAlbumQueryMode( AlbumQueryMode mode ) +{ + d->albumQueryMode = mode; +} + #include "MemoryQueryMaker.moc" diff --git a/src/collection/support/MemoryQueryMaker.h b/src/collection/support/MemoryQueryMaker.h index e7c156b..3fbb061 100644 --- a/src/collection/support/MemoryQueryMaker.h +++ b/src/collection/support/MemoryQueryMaker.h @@ -74,6 +74,8 @@ class AMAROK_EXPORT MemoryQueryMaker : public QueryMaker virtual QueryMaker* beginOr(); virtual QueryMaker* endAndOr(); + virtual QueryMaker* setAlbumQueryMode( AlbumQueryMode mode ); + //MemoryQueryMaker specific methods void runQuery(); void handleResult(); diff --git a/src/images/pud_items.svg b/src/images/pud_items.svg index 7be6650..675eb22 100644 --- a/src/images/pud_items.svg +++ b/src/images/pud_items.svg @@ -29,8 +29,8 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +