File indexing completed on 2024-05-05 04:49:21

0001 /****************************************************************************************
0002  * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz>                                      *
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) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #ifndef STATSYNCING_CONTROLLER_H
0018 #define STATSYNCING_CONTROLLER_H
0019 
0020 #include "amarok_export.h"
0021 // for CollectionManager::CollectionStatus that cannot be fwd-declared
0022 #include "core-impl/collections/support/CollectionManager.h"
0023 
0024 #include <QPointer>
0025 #include <QDateTime>
0026 #include <QMap>
0027 
0028 #include <KLocalizedString>
0029 
0030 class QTimer;
0031 
0032 namespace StatSyncing
0033 {
0034     class Config;
0035     class CreateProviderDialog;
0036     class Process;
0037     class Provider;
0038     typedef QSharedPointer<Provider> ProviderPtr;
0039     typedef QList<ProviderPtr> ProviderPtrList;
0040     class ProviderFactory;
0041     class ScrobblingService;
0042     typedef QSharedPointer<ScrobblingService> ScrobblingServicePtr;
0043 
0044     /**
0045      * A singleton class that controls statistics synchronization and related tasks.
0046      */
0047     class AMAROK_EXPORT Controller : public QObject
0048     {
0049         Q_OBJECT
0050 
0051         public:
0052             explicit Controller( QObject *parent = nullptr );
0053             ~Controller() override;
0054 
0055             /**
0056              * Return a list of Meta::val* fields that statistics synchronization can
0057              * actually synchronize.
0058              */
0059             static QList<qint64> availableFields();
0060 
0061             /**
0062              * Register a StatSyncing::Provider with StatSyncing controller. This makes
0063              * it possible to synchronize provider with other providers. You don't need
0064              * to call this for Collections that are registered through CollectionManager
0065              * (and marked as enabled there) as it is done automatically.
0066              */
0067             virtual void registerProvider( const ProviderPtr &provider );
0068 
0069             /**
0070              * Forget about StatSyncing::Provider @p provider.
0071              * @param provider the provider
0072              */
0073             virtual void unregisterProvider( const ProviderPtr &provider );
0074 
0075             /**
0076              * Handle plugin factories derived from ProviderFactory, used for creating
0077              * multiple provider instances. This method is called by Amarok's plugin
0078              * infrastructure.
0079              */
0080             void setFactories( const QList<QSharedPointer<Plugins::PluginFactory> > &factories );
0081 
0082             /**
0083              * Returns true if any instantiatable provider types are registered with the
0084              * controller.
0085              */
0086             bool hasProviderFactories() const;
0087 
0088             /**
0089              * Returns true if the provider identified by @param id is configurable
0090              */
0091             bool providerIsConfigurable( const QString &id ) const;
0092 
0093             /**
0094              * Returns a configuration dialog for a provider identified by @param id .
0095              * @returns 0 if there's no provider identified by id or the provider is not
0096              * configurable, otherwise a pointer to the dialog constructed as a child of
0097              * The::mainWindow
0098              */
0099             QWidget *providerConfigDialog( const QString &id ) const;
0100 
0101             /**
0102              * Returns a provider creation dialog, prepopulated with registered provider
0103              * types.
0104              * @returns a pointer to the dialog constructed as a child of The::mainWindow,
0105              * and is a subclass of KAssistantDialog.
0106              *
0107              * @see StatSyncing::CreateProviderDialog
0108              */
0109             QWidget *providerCreationDialog() const;
0110 
0111             /**
0112              * Register ScrobblingService with StatSyncing controller. Controller then
0113              * listens to EngineController and calls scrobble() etc. when user plays
0114              * tracks. Also allows scrobbling for tracks played on just connected iPods.
0115              *
0116              * @param service
0117              */
0118             void registerScrobblingService( const ScrobblingServicePtr &service );
0119 
0120             /**
0121              * Forget about ScrobblingService @param service
0122              */
0123             void unregisterScrobblingService( const ScrobblingServicePtr &service );
0124 
0125             /**
0126              * Return a list of currently registered scrobbling services (in arbitrary
0127              * order).
0128              */
0129             QList<ScrobblingServicePtr> scrobblingServices() const;
0130 
0131             /**
0132              * Return StatSyncing configuration object that describes enabled and
0133              * disabled statsyncing providers. You may not cache the pointer.
0134              */
0135             Config *config();
0136 
0137         public Q_SLOTS:
0138             /**
0139              * Start the whole synchronization machinery. This call returns quickly,
0140              * way before the synchronization is finished.
0141              */
0142             void synchronize();
0143 
0144             /**
0145              * Scrobble a track using all registered scrobbling services. They may check
0146              * certain criteria such as track length and refuse to scrobble the track.
0147              *
0148              * @param track track to scrobble
0149              * @param playedFraction fraction which has been actually played, or a number
0150              *                       greater than 1 if the track was played multiple times
0151              *                       (for example on a media device)
0152              * @param time time when it was played, invalid QDateTime signifies that the
0153              *             track has been played just now. This is the default when the
0154              *             parameter is omitted.
0155              */
0156             void scrobble( const Meta::TrackPtr &track, double playedFraction = 1.0,
0157                            const QDateTime &time = QDateTime() );
0158 
0159         Q_SIGNALS:
0160             /**
0161              * Emitted when a track passed to scrobble() is successfully queued for
0162              * scrobbling submission. This signal is emitted for every scrobbling service.
0163              * For each service, you either get this or scrobbleFailed().
0164              */
0165             void trackScrobbled( const ScrobblingServicePtr &service, const Meta::TrackPtr &track );
0166 
0167             /**
0168              * Emitted when a scrobbling service @p service was unable to scrobble() a track.
0169              *
0170              * @param service the service
0171              * @param track the track
0172              * @param error is a ScrobblingService::ScrobbleError enum value.
0173              */
0174             void scrobbleFailed( const ScrobblingServicePtr &service, const Meta::TrackPtr &track, int error );
0175 
0176         private Q_SLOTS:
0177             /**
0178              * Creates new instance of provider type identified by @param type
0179              * with configuration stored in @param config.
0180              */
0181             void createProvider( const QString &type, const QVariantMap &config );
0182 
0183             /**
0184              * Reconfigures provider identified by @param id with configuration
0185              * stored in @param config.
0186              */
0187             void reconfigureProvider( const QString &id, const QVariantMap &config );
0188 
0189             /**
0190              * Can only be connected to provider changed() signal
0191              */
0192             void slotProviderUpdated();
0193             /**
0194              * Wait a few seconds and if no collectionUpdate() signal arrives until then,
0195              * start synchronization. Otherwise postpone the synchronization for a few
0196              * seconds.
0197              */
0198             void delayedStartSynchronization();
0199             void slotCollectionAdded( Collections::Collection* collection,
0200                                       CollectionManager::CollectionStatus status );
0201             void slotCollectionRemoved( const QString &id );
0202             void startNonInteractiveSynchronization();
0203             void synchronizeWithMode( int mode );
0204 
0205             void slotTrackFinishedPlaying( const Meta::TrackPtr &track, double playedFraction );
0206             void slotResetLastSubmittedNowPlayingTrack();
0207             void slotUpdateNowPlayingWithCurrentTrack();
0208 
0209         private:
0210             Q_DISABLE_COPY( Controller )
0211 
0212             ProviderPtr findRegisteredProvider( const QString &id ) const;
0213 
0214             /**
0215              * Return true if important metadata of both tracks is equal.
0216              */
0217             bool tracksVirtuallyEqual( const Meta::TrackPtr &first, const Meta::TrackPtr &second );
0218             QMap<QString, QSharedPointer<ProviderFactory> > m_providerFactories;
0219 
0220             // synchronization-related
0221             ProviderPtrList m_providers;
0222             QPointer<Process> m_currentProcess;
0223             QTimer *m_startSyncingTimer;
0224             Config *m_config;
0225 
0226             /**
0227              * When a new collection appears, StatSyncing::Controller will automatically
0228              * trigger synchronization. It however waits s_syncingTriggerTimeout
0229              * milliseconds to let the collection settle down. Moreover, if the newly
0230              * added collection emits updated(), the timeout will run from start again.
0231              *
0232              * (reason: e.g. iPod Collection appears quickly, but with no tracks, which
0233              * are added gradually as they are parsed. This "ensures" we only start
0234              * syncing as soon as all tracks are parsed.)
0235              */
0236             static const int s_syncingTriggerTimeout;
0237 
0238             // scrobbling-related
0239             QList<ScrobblingServicePtr> m_scrobblingServices;
0240             QTimer *m_updateNowPlayingTimer;
0241             Meta::TrackPtr m_lastSubmittedNowPlayingTrack;
0242     };
0243 
0244 } // namespace StatSyncing
0245 
0246 #endif // STATSYNCING_CONTROLLER_H