1 /**************************************************************************************** 2 * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz> * 3 * * 4 * This program is free software; you can redistribute it and/or modify it under * 5 * the terms of the GNU General Public License as published by the Free Software * 6 * Foundation; either version 2 of the License, or (at your option) any later * 7 * version. * 8 * * 9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 12 * * 13 * You should have received a copy of the GNU General Public License along with * 14 * this program. If not, see <http://www.gnu.org/licenses/>. * 15 ****************************************************************************************/ 16 17 #ifndef STATSYNCING_CONTROLLER_H 18 #define STATSYNCING_CONTROLLER_H 19 20 #include "amarok_export.h" 21 // for CollectionManager::CollectionStatus that cannot be fwd-declared 22 #include "core-impl/collections/support/CollectionManager.h" 23 24 #include <QPointer> 25 #include <QDateTime> 26 #include <QMap> 27 28 #include <KLocalizedString> 29 30 class QTimer; 31 32 namespace StatSyncing 33 { 34 class Config; 35 class CreateProviderDialog; 36 class Process; 37 class Provider; 38 typedef QSharedPointer<Provider> ProviderPtr; 39 typedef QList<ProviderPtr> ProviderPtrList; 40 class ProviderFactory; 41 class ScrobblingService; 42 typedef QSharedPointer<ScrobblingService> ScrobblingServicePtr; 43 44 /** 45 * A singleton class that controls statistics synchronization and related tasks. 46 */ 47 class AMAROK_EXPORT Controller : public QObject 48 { 49 Q_OBJECT 50 51 public: 52 explicit Controller( QObject *parent = nullptr ); 53 ~Controller() override; 54 55 /** 56 * Return a list of Meta::val* fields that statistics synchronization can 57 * actually synchronize. 58 */ 59 static QList<qint64> availableFields(); 60 61 /** 62 * Register a StatSyncing::Provider with StatSyncing controller. This makes 63 * it possible to synchronize provider with other providers. You don't need 64 * to call this for Collections that are registered through CollectionManager 65 * (and marked as enabled there) as it is done automatically. 66 */ 67 virtual void registerProvider( const ProviderPtr &provider ); 68 69 /** 70 * Forget about StatSyncing::Provider @p provider. 71 * @param provider the provider 72 */ 73 virtual void unregisterProvider( const ProviderPtr &provider ); 74 75 /** 76 * Handle plugin factories derived from ProviderFactory, used for creating 77 * multiple provider instances. This method is called by Amarok's plugin 78 * infrastructure. 79 */ 80 void setFactories( const QList<QSharedPointer<Plugins::PluginFactory> > &factories ); 81 82 /** 83 * Returns true if any instantiatable provider types are registered with the 84 * controller. 85 */ 86 bool hasProviderFactories() const; 87 88 /** 89 * Returns true if the provider identified by @param id is configurable 90 */ 91 bool providerIsConfigurable( const QString &id ) const; 92 93 /** 94 * Returns a configuration dialog for a provider identified by @param id . 95 * @returns 0 if there's no provider identified by id or the provider is not 96 * configurable, otherwise a pointer to the dialog constructed as a child of 97 * The::mainWindow 98 */ 99 QWidget *providerConfigDialog( const QString &id ) const; 100 101 /** 102 * Returns a provider creation dialog, prepopulated with registered provider 103 * types. 104 * @returns a pointer to the dialog constructed as a child of The::mainWindow, 105 * and is a subclass of KAssistantDialog. 106 * 107 * @see StatSyncing::CreateProviderDialog 108 */ 109 QWidget *providerCreationDialog() const; 110 111 /** 112 * Register ScrobblingService with StatSyncing controller. Controller then 113 * listens to EngineController and calls scrobble() etc. when user plays 114 * tracks. Also allows scrobbling for tracks played on just connected iPods. 115 * 116 * @param service 117 */ 118 void registerScrobblingService( const ScrobblingServicePtr &service ); 119 120 /** 121 * Forget about ScrobblingService @param service 122 */ 123 void unregisterScrobblingService( const ScrobblingServicePtr &service ); 124 125 /** 126 * Return a list of currently registered scrobbling services (in arbitrary 127 * order). 128 */ 129 QList<ScrobblingServicePtr> scrobblingServices() const; 130 131 /** 132 * Return StatSyncing configuration object that describes enabled and 133 * disabled statsyncing providers. You may not cache the pointer. 134 */ 135 Config *config(); 136 137 public Q_SLOTS: 138 /** 139 * Start the whole synchronization machinery. This call returns quickly, 140 * way before the synchronization is finished. 141 */ 142 void synchronize(); 143 144 /** 145 * Scrobble a track using all registered scrobbling services. They may check 146 * certain criteria such as track length and refuse to scrobble the track. 147 * 148 * @param track track to scrobble 149 * @param playedFraction fraction which has been actually played, or a number 150 * greater than 1 if the track was played multiple times 151 * (for example on a media device) 152 * @param time time when it was played, invalid QDateTime signifies that the 153 * track has been played just now. This is the default when the 154 * parameter is omitted. 155 */ 156 void scrobble( const Meta::TrackPtr &track, double playedFraction = 1.0, 157 const QDateTime &time = QDateTime() ); 158 159 Q_SIGNALS: 160 /** 161 * Emitted when a track passed to scrobble() is successfully queued for 162 * scrobbling submission. This signal is emitted for every scrobbling service. 163 * For each service, you either get this or scrobbleFailed(). 164 */ 165 void trackScrobbled( const ScrobblingServicePtr &service, const Meta::TrackPtr &track ); 166 167 /** 168 * Emitted when a scrobbling service @p service was unable to scrobble() a track. 169 * 170 * @param service the service 171 * @param track the track 172 * @param error is a ScrobblingService::ScrobbleError enum value. 173 */ 174 void scrobbleFailed( const ScrobblingServicePtr &service, const Meta::TrackPtr &track, int error ); 175 176 private Q_SLOTS: 177 /** 178 * Creates new instance of provider type identified by @param type 179 * with configuration stored in @param config. 180 */ 181 void createProvider( const QString &type, const QVariantMap &config ); 182 183 /** 184 * Reconfigures provider identified by @param id with configuration 185 * stored in @param config. 186 */ 187 void reconfigureProvider( const QString &id, const QVariantMap &config ); 188 189 /** 190 * Can only be connected to provider changed() signal 191 */ 192 void slotProviderUpdated(); 193 /** 194 * Wait a few seconds and if no collectionUpdate() signal arrives until then, 195 * start synchronization. Otherwise postpone the synchronization for a few 196 * seconds. 197 */ 198 void delayedStartSynchronization(); 199 void slotCollectionAdded( Collections::Collection* collection, 200 CollectionManager::CollectionStatus status ); 201 void slotCollectionRemoved( const QString &id ); 202 void startNonInteractiveSynchronization(); 203 void synchronizeWithMode( int mode ); 204 205 void slotTrackFinishedPlaying( const Meta::TrackPtr &track, double playedFraction ); 206 void slotResetLastSubmittedNowPlayingTrack(); 207 void slotUpdateNowPlayingWithCurrentTrack(); 208 209 private: 210 Q_DISABLE_COPY( Controller ) 211 212 ProviderPtr findRegisteredProvider( const QString &id ) const; 213 214 /** 215 * Return true if important metadata of both tracks is equal. 216 */ 217 bool tracksVirtuallyEqual( const Meta::TrackPtr &first, const Meta::TrackPtr &second ); 218 QMap<QString, QSharedPointer<ProviderFactory> > m_providerFactories; 219 220 // synchronization-related 221 ProviderPtrList m_providers; 222 QPointer<Process> m_currentProcess; 223 QTimer *m_startSyncingTimer; 224 Config *m_config; 225 226 /** 227 * When a new collection appears, StatSyncing::Controller will automatically 228 * trigger synchronization. It however waits s_syncingTriggerTimeout 229 * milliseconds to let the collection settle down. Moreover, if the newly 230 * added collection emits updated(), the timeout will run from start again. 231 * 232 * (reason: e.g. iPod Collection appears quickly, but with no tracks, which 233 * are added gradually as they are parsed. This "ensures" we only start 234 * syncing as soon as all tracks are parsed.) 235 */ 236 static const int s_syncingTriggerTimeout; 237 238 // scrobbling-related 239 QList<ScrobblingServicePtr> m_scrobblingServices; 240 QTimer *m_updateNowPlayingTimer; 241 Meta::TrackPtr m_lastSubmittedNowPlayingTrack; 242 }; 243 244 } // namespace StatSyncing 245 246 #endif // STATSYNCING_CONTROLLER_H 247