File indexing completed on 2025-01-05 04:26:02

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Alejandro Wainzinger <aikawarazuni@gmail.com>                     *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) version 3 or        *
0007  * any later version accepted by the membership of KDE e.V. (or its successor approved  *
0008  * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of  *
0009  * version 3 of the license.                                                            *
0010  *                                                                                      *
0011  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0013  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0014  *                                                                                      *
0015  * You should have received a copy of the GNU General Public License along with         *
0016  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0017  ****************************************************************************************/
0018 
0019 #ifndef MEDIADEVICEHANDLER_H
0020 #define MEDIADEVICEHANDLER_H
0021 
0022 #include "core/meta/Observer.h"
0023 #include "core-impl/collections/mediadevicecollection/MediaDeviceMeta.h"
0024 #include "core-impl/collections/mediadevicecollection/handler/MediaDeviceHandlerCapability.h"
0025 #include "core-impl/collections/mediadevicecollection/handler/capabilities/PlaylistCapability.h"
0026 #include "core-impl/collections/mediadevicecollection/handler/capabilities/PodcastCapability.h"
0027 #include "core-impl/collections/mediadevicecollection/handler/capabilities/ReadCapability.h"
0028 #include "core-impl/collections/mediadevicecollection/handler/capabilities/WriteCapability.h"
0029 #include "core-impl/collections/mediadevicecollection/playlist/MediaDevicePlaylist.h"
0030 #include "core-impl/collections/mediadevicecollection/playlist/MediaDeviceUserPlaylistProvider.h"
0031 #include "core-impl/collections/support/MemoryCollection.h"
0032 #include "core-impl/playlists/providers/user/UserPlaylistProvider.h"
0033 
0034 #include <ThreadWeaver/Job>
0035 
0036 #include <QAction>
0037 #include <QObject>
0038 #include <QMap>
0039 #include <QMultiMap>
0040 
0041 class QString;
0042 class QMutex;
0043 
0044 namespace Collections {
0045     class MediaDeviceCollection;
0046 }
0047 
0048 namespace Meta
0049 {
0050     typedef QMultiMap<QString, Meta::TrackPtr> TitleMap;
0051 
0052     class MEDIADEVICECOLLECTION_EXPORT MetaHandlerCapability
0053     {
0054     public:
0055         virtual ~MetaHandlerCapability() {}
0056 
0057         virtual bool hasCapabilityInterface( Handler::Capability::Type type ) const;
0058 
0059         virtual Handler::Capability* createCapabilityInterface( Handler::Capability::Type type );
0060 
0061         /**
0062              * Retrieves a specialized interface which represents a capability of this
0063              * object.
0064              *
0065              * @returns a pointer to the capability interface if it exists, 0 otherwise
0066              */
0067         template <class CapIface> CapIface *create()
0068         {
0069             Handler::Capability::Type type = CapIface::capabilityInterfaceType();
0070             Handler::Capability *iface = createCapabilityInterface(type);
0071             return qobject_cast<CapIface *>(iface);
0072         }
0073 
0074         /**
0075              * Tests if an object provides a given capability interface.
0076              *
0077              * @returns true if the interface is available, false otherwise
0078              */
0079         template <class CapIface> bool is() const
0080         {
0081             return hasCapabilityInterface( CapIface::capabilityInterfaceType() );
0082         }
0083     };
0084 
0085 /**
0086 The MediaDeviceHandler is the backend where all low-level library calls are made.
0087 It exists to leave a generic API in the other classes, while allowing for low-level
0088 calls to be isolated here.
0089 */
0090 
0091 class MEDIADEVICECOLLECTION_EXPORT MediaDeviceHandler : public QObject, public Meta::MetaHandlerCapability, public Meta::Observer
0092 {
0093     Q_OBJECT
0094 
0095 public:
0096     /**
0097     * Destructor
0098     */
0099     ~MediaDeviceHandler() override;
0100 
0101     // Declare thread as friend class
0102 
0103     friend class ParseWorkerThread;
0104 
0105     /**
0106     * Begins an attempt to connect to the device, and emits
0107     * attemptConnectionDone when it finishes.
0108     */
0109     virtual void init() = 0; // collection
0110 
0111     /**
0112     * Checks if the handler successfully connected
0113     * to the device.
0114     * @return
0115     *   TRUE if the device was successfully connected to
0116     *   FALSE if the device was not successfully connected to
0117     */
0118     bool succeeded() const // collection
0119     {
0120         return m_success;
0121     }
0122 
0123     /// Methods provided for CollectionLocation
0124 
0125     /**
0126     * Checks if a device can be written to.
0127     * @return
0128     *   TRUE if the device can be written to
0129     *   FALSE if the device can not be written to
0130     */
0131     virtual bool isWritable() const = 0;
0132 
0133     /** Given a list of tracks, get URLs for device tracks
0134     *  of this type of device.  If the device needs to
0135     *  do some work to get URLs (e.g. copy tracks to a
0136     *  temporary location) the overridden method in
0137     *  the handler takes care of it, but must Q_EMIT
0138     *  gotCopyableUrls when finished.
0139     *  @param tracks The list of tracks for which to fetch urls
0140     */
0141     virtual void getCopyableUrls( const Meta::TrackList &tracks );
0142 
0143     /**
0144     * Fetches the human-readable name of the device.
0145     * This is often called from the Collection since
0146     * a library call is needed to get this name.
0147     * @return A QString with the name
0148     */
0149     virtual QString prettyName() const = 0;
0150 
0151     /**
0152     * Copies a list of tracks to the device.
0153     * @param tracklist The list of tracks to copy.
0154     */
0155     void copyTrackListToDevice( const Meta::TrackList tracklist );
0156 
0157     /**
0158     * Removes a list of tracks from the device.
0159     * @param tracks The list of tracks to remove.
0160     */
0161     void removeTrackListFromDevice( const Meta::TrackList &tracks );
0162 
0163     /** This function is called just before a track in the playlist is to be played, and gives
0164     *  a chance for e.g. MTP to copy the track to a temporary location, set a playable url,
0165     *  to emulate the track actually being played off the device
0166     *  @param track The track that needs to prepare to be played
0167     */
0168     virtual void prepareToPlay( Meta::MediaDeviceTrackPtr &track ) { Q_UNUSED( track ) } // called by @param track
0169 
0170     virtual float usedcapacity();
0171     virtual float totalcapacity();
0172 
0173     Playlists::UserPlaylistProvider* provider();
0174 
0175     // HACK: Used for device-specific actions, such as initialize for iPod
0176     virtual QList<QAction *> collectionActions() { return QList<QAction*> (); }
0177 
0178 Q_SIGNALS:
0179     void gotCopyableUrls( const QMap<Meta::TrackPtr, QUrl> &urls );
0180     void databaseWritten( bool succeeded );
0181 
0182     void deleteTracksDone();
0183     void incrementProgress();
0184     void endProgressOperation( QObject *owner );
0185 
0186     void copyTracksDone( bool success );
0187     void removeTracksDone();
0188 
0189     /* File I/O Methods */
0190 
0191 public Q_SLOTS:
0192 
0193    /**
0194     * Parses the media device's database and creates a Meta::MediaDeviceTrack
0195     * for each track in the database.  NOTE: only call once per device.
0196     */
0197     void parseTracks(); // collection
0198 
0199    /**
0200     * Writes to the device's database if it has one, otherwise
0201     * simply calls slotDatabaseWritten to continue the workflow.
0202     */
0203 
0204     virtual void writeDatabase() { slotDatabaseWritten( true ); }
0205 
0206     void savePlaylist( const Playlists::MediaDevicePlaylistPtr &playlist, const QString& name );
0207     void renamePlaylist( const Playlists::MediaDevicePlaylistPtr &playlist );
0208     void deletePlaylists( const Playlists::MediaDevicePlaylistList &playlistlist );
0209 
0210     bool privateParseTracks();
0211 
0212     void copyNextTrackToDevice();
0213     bool privateCopyTrackToDevice( const Meta::TrackPtr& track );
0214 
0215     void removeNextTrackFromDevice();
0216     void privateRemoveTrackFromDevice( const Meta::TrackPtr &track );
0217 
0218     void slotCopyNextTrackFailed( ThreadWeaver::JobPointer job, const Meta::TrackPtr& track );
0219     void slotCopyNextTrackDone( ThreadWeaver::JobPointer job, const Meta::TrackPtr& track );
0220 
0221 protected:
0222 
0223     /**
0224      * Constructor
0225      * @param parent the Collection whose handler this is
0226      */
0227     MediaDeviceHandler( QObject *parent );
0228 
0229     /**
0230      * Creates a MediaDeviceTrack based on the latest track struct created as a
0231      *  result of a copy to the device, and adds it into the collection to reflect
0232      *  that it has been copied.
0233      *  @param track The track to add to the collection
0234      */
0235     void addMediaDeviceTrackToCollection( Meta::MediaDeviceTrackPtr &track );
0236 
0237     /**
0238      * Removes the @param track from all the collection's maps to reflect that
0239      * it has been removed from the collection
0240      * @param track The track to remove from the collection
0241      */
0242     void removeMediaDeviceTrackFromCollection( Meta::MediaDeviceTrackPtr &track );
0243 
0244     /**
0245      * Uses wrapped libGet methods to fill a track with information from device
0246      * @param track The track from whose associated struct to get the information
0247      * @param destTrack The track that we want to fill with information
0248      */
0249     void getBasicMediaDeviceTrackInfo( const Meta::MediaDeviceTrackPtr& track, Meta::MediaDeviceTrackPtr destTrack );
0250 
0251     /**
0252      * Uses wrapped libSet methods to fill a track struct of the particular library
0253      * with information from a Meta::Track
0254      * @param srcTrack The track that has the source information
0255      * @param destTrack The track whose associated struct we want to fill with information
0256      */
0257     void setBasicMediaDeviceTrackInfo( const Meta::TrackPtr &srcTrack, Meta::MediaDeviceTrackPtr destTrack );
0258 
0259     Collections::MediaDeviceCollection   *m_memColl; ///< Associated collection
0260 
0261     bool m_success;
0262     bool m_copyingthreadsafe; ///< whether or not the handler's method of copying is threadsafe
0263     TitleMap          m_titlemap; ///< Map of track titles to tracks, used to detect duplicates
0264 
0265 protected Q_SLOTS:
0266 
0267     void slotFinalizeTrackCopy( const Meta::TrackPtr & track );
0268     void slotCopyTrackFailed( const Meta::TrackPtr & track );
0269     void slotFinalizeTrackRemove( const Meta::TrackPtr & track );
0270 
0271     void slotDatabaseWritten( bool success );
0272 
0273     void enqueueNextCopyThread();
0274 
0275     void slotDeletingHandler();
0276 
0277 private:
0278 
0279     /**
0280     * Pulls out meta information (e.g. artist string)
0281     * from track struct, inserts into appropriate map
0282     * (e.g. ArtistMap).  Sets track's meta info
0283     * (e.g. artist string) to that extracted from
0284     * track struct's.
0285     * @param track - track being written to
0286     * @param Map - map where meta information is
0287     * associated to appropriate meta pointer
0288     * (e.g. QString artist, ArtistPtr )
0289     */
0290 
0291     void setupArtistMap( Meta::MediaDeviceTrackPtr track, ArtistMap &artistMap );
0292     void setupAlbumMap( Meta::MediaDeviceTrackPtr track, AlbumMap &albumMap, ArtistMap &artistMap );
0293     void setupGenreMap( Meta::MediaDeviceTrackPtr track, GenreMap &genreMap );
0294     void setupComposerMap( Meta::MediaDeviceTrackPtr track, ComposerMap &composerMap );
0295     void setupYearMap( Meta::MediaDeviceTrackPtr track, YearMap &yearMap );
0296 
0297     // Misc. Helper Methods
0298 
0299     /**
0300      * Tries to create read capability in m_rc
0301      * @return true if m_rc is valid read capability, false otherwise
0302      */
0303     bool setupReadCapability();
0304 
0305     /**
0306      * Tries to create write capability in m_rc
0307      * @return true if m_wc is valid write capability, false otherwise
0308      */
0309     bool setupWriteCapability();
0310 
0311     /**
0312      *  @return free space on the device
0313      */
0314     float freeSpace();
0315 
0316     // Observer Methods
0317 
0318     /** These methods are called when the metadata of a track has changed. They invoke an MediaDevice DB update */
0319     void metadataChanged( const Meta::TrackPtr &track ) override;
0320     void metadataChanged( const Meta::ArtistPtr &artist ) override;
0321     void metadataChanged( const Meta::AlbumPtr &album ) override;
0322     void metadataChanged( const Meta::GenrePtr &genre ) override;
0323     void metadataChanged( const Meta::ComposerPtr &composer ) override;
0324     void metadataChanged(const YearPtr &year ) override;
0325 
0326     /**
0327     * Handler Variables
0328     */
0329 
0330     Playlists::MediaDeviceUserPlaylistProvider *m_provider; ///< Associated playlist provider
0331 
0332     bool m_copyFailed; ///< Indicates whether a copy failed or not
0333     bool m_isCopying;
0334     bool m_isDeleting;
0335 
0336     Meta::TrackList   m_tracksToCopy; ///< List of tracks left to copy
0337     Meta::TrackList   m_tracksCopying; ///< List of tracks currently copying
0338     Meta::TrackList   m_tracksToDelete; ///< List of tracks left to delete
0339 
0340     int m_numTracksToCopy; ///< The number of tracks left to copy
0341     int m_numTracksToRemove; ///< The number of tracks left to remove
0342 
0343     QMap<Meta::TrackPtr, QString> m_tracksFailed; ///< tracks that failed to copy
0344     QHash<Meta::TrackPtr, Meta::MediaDeviceTrackPtr> m_trackSrcDst; ///< points source to destTracks, for completion of addition to collection
0345 
0346     QMutex m_mutex; ///< A make certain operations atomic when threads are at play
0347 
0348     // Capability-related variables
0349 
0350     Handler::PlaylistCapability *m_pc;
0351     Handler::PodcastCapability *m_podcastCapability;
0352     Handler::ReadCapability *m_rc;
0353     Handler::WriteCapability *m_wc;
0354 };
0355 
0356 /**
0357 * The ParseWorkerThread is used to run a full parse of the device's database in
0358 * a separate thread. Once done, it informs the Collection it is done
0359 */
0360 
0361 class ParseWorkerThread : public QObject , public ThreadWeaver::Job
0362 {
0363     Q_OBJECT
0364 public:
0365     /**
0366     * The constructor.
0367     * @param handler The handler
0368     */
0369 
0370     explicit ParseWorkerThread( MediaDeviceHandler* handler);
0371 
0372     /**
0373     * The destructor.
0374     */
0375 
0376     ~ParseWorkerThread() override;
0377 
0378     /**
0379     * Sees the success variable, which says whether or not the copy completed
0380 successfully.
0381     * @return Whether or not the copy was successful, i.e. m_success
0382     */
0383 
0384     bool success() const override;
0385 
0386 Q_SIGNALS:
0387     /** This signal is emitted when this job is being processed by a thread. */
0388     void started(ThreadWeaver::JobPointer);
0389     /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */
0390     void done(ThreadWeaver::JobPointer);
0391     /** This job has failed.
0392      * This signal is emitted when success() returns false after the job is executed. */
0393     void failed(ThreadWeaver::JobPointer);
0394 
0395 private Q_SLOTS:
0396     /**
0397     * Is called when the job is done successfully, and simply
0398     * calls Collection's emitCollectionReady()
0399     * @param job The job that was done
0400     */
0401 
0402     void slotDoneSuccess( ThreadWeaver::JobPointer );
0403 
0404 protected:
0405     /**
0406     * Reimplemented, simply runs the parse method.
0407     */
0408     void run(ThreadWeaver::JobPointer self = QSharedPointer<ThreadWeaver::Job>(), ThreadWeaver::Thread *thread = nullptr) override;
0409     void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) override;
0410     void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) override;
0411 
0412 private:
0413     bool m_success; ///< Whether or not the parse was successful
0414     MediaDeviceHandler *m_handler; ///< The handler
0415 };
0416 
0417 /**
0418 * The CopyWorkerThread is used to run a copy operation on a single track in a separate thread.
0419 * Copying is generally done one thread at a time so as to not hurt performance, and because
0420 * many copying mechanisms like that of libmtp can only copy one file at a time.  Copying
0421 * methods that are not threadsafe should not use CopyWorkerThread, and should set the
0422 * Handler's m_copyingthreadsafe variable to false in the Handler's constructor.
0423 */
0424 
0425 class CopyWorkerThread : public QObject, public ThreadWeaver::Job
0426 {
0427     Q_OBJECT
0428 public:
0429     /**
0430     * The constructor.
0431     * @param track The source track to copy from
0432     * @param handler The handler
0433     */
0434 
0435     CopyWorkerThread( const Meta::TrackPtr &track, MediaDeviceHandler* handler );
0436 
0437     /**
0438     * The destructor.
0439     */
0440 
0441     ~CopyWorkerThread() override;
0442 
0443     /**
0444     * Sets the success variable, which says whether or not the copy completed successfully.
0445     * @return Whether or not the copy was successful, i.e. m_success
0446     */
0447 
0448     bool success() const override;
0449 
0450 Q_SIGNALS:
0451 
0452     /**
0453     * Is emitted when the job is done successfully
0454     * Parameters:
0455     * The job that was done
0456     * @param track The source track used for the copy
0457     */
0458 
0459     void copyTrackDone( ThreadWeaver::JobPointer, const Meta::TrackPtr& track );
0460 
0461     /**
0462     * Is emitted when the job is done and has failed
0463     * Parameters:
0464     * The job that was done
0465     * @param track The source track used for the copy
0466     */
0467     void copyTrackFailed( ThreadWeaver::JobPointer, const Meta::TrackPtr& track );
0468 
0469     /** This signal is emitted when this job is being processed by a thread. */
0470     void started(ThreadWeaver::JobPointer);
0471     /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */
0472     void done(ThreadWeaver::JobPointer);
0473     /** This job has failed.
0474      * This signal is emitted when success() returns false after the job is executed. */
0475     void failed(ThreadWeaver::JobPointer);
0476 
0477 private Q_SLOTS:
0478     /**
0479     * Is called when the job is done successfully, and simply
0480     * emits copyTrackDone
0481     * @param job The job that was done
0482     */
0483 
0484     void slotDoneSuccess( ThreadWeaver::JobPointer );
0485 
0486     /**
0487     * Is called when the job is done and failed, and simply
0488     * emits copyTrackFailed
0489     * @param job The job that was done
0490     */
0491 
0492     void slotDoneFailed( ThreadWeaver::JobPointer );
0493 
0494 protected:
0495     /**
0496     * Reimplemented, simply runs the copy track method.
0497     */
0498     void run(ThreadWeaver::JobPointer self = QSharedPointer<ThreadWeaver::Job>(), ThreadWeaver::Thread *thread = nullptr) override;
0499     void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) override;
0500     void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) override;
0501 
0502 private:
0503     bool m_success; ///< Whether or not the copy was successful
0504     Meta::TrackPtr m_track; ///< The source track to copy from
0505     MediaDeviceHandler *m_handler; ///< The handler
0506 };
0507 
0508 }
0509 #endif