File indexing completed on 2024-05-19 04:49:36

0001 /***********************************************************************
0002  * Copyright 2010  Canonical Ltd
0003  *   (author: Aurelien Gateau <aurelien.gateau@canonical.com>)
0004  * Copyright 2012  Eike Hein <hein@kde.org>
0005  * Copyright 2012  Alex Merry <alex.merry@kdemail.net>
0006  *
0007  * This program is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU General Public License as
0009  * published by the Free Software Foundation; either version 2 of
0010  * the License or (at your option) version 3 or any later version
0011  * accepted by the membership of KDE e.V. (or its successor approved
0012  * by the membership of KDE e.V.), which shall act as a proxy
0013  * defined in Section 14 of version 3 of the license.
0014  *
0015  * This program is distributed in the hope that it will be useful,
0016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018  * GNU General Public License for more details.
0019  *
0020  * You should have received a copy of the GNU General Public License
0021  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0022  ***********************************************************************/
0023 
0024 #include "MediaPlayer2Player.h"
0025 
0026 #include "EngineController.h"
0027 #include "amarokconfig.h"
0028 #include "core/meta/Meta.h"
0029 #include "core/meta/support/MetaUtility.h"
0030 #include "core/support/Debug.h"
0031 #include "playlist/PlaylistActions.h"
0032 #include "playlist/PlaylistController.h"
0033 #include "playlist/PlaylistModelStack.h"
0034 
0035 #include <QUrl>
0036 
0037 static QDBusObjectPath mprisTrackId(quint64 playlistTrackId)
0038 {
0039     QString path;
0040     if( playlistTrackId > 0 ) {
0041         path = QStringLiteral( "/org/kde/amarok/Track/%1" ).arg( playlistTrackId );
0042     } else {
0043         // dropped out of the playlist
0044         path = QStringLiteral( "/org/kde/amarok/OrphanTrack" );
0045     }
0046     return QDBusObjectPath( path );
0047 }
0048 
0049 static QDBusObjectPath activeMprisTrackId()
0050 {
0051     return mprisTrackId( The::playlist()->activeId() );
0052 }
0053 
0054 using namespace Amarok;
0055 
0056 MediaPlayer2Player::MediaPlayer2Player(QObject* parent)
0057     : DBusAbstractAdaptor(parent)
0058     , m_lastPosition(-1)
0059 {
0060     connect( The::engineController(), &EngineController::trackPositionChanged,
0061              this, &MediaPlayer2Player::trackPositionChanged );
0062     // it is important that we receive this signal *after* the playlist code
0063     // has dealt with it, in order to get the right value for mpris:trackid
0064     connect( The::engineController(), &EngineController::trackChanged,
0065              this, &MediaPlayer2Player::trackChanged,
0066              Qt::QueuedConnection );
0067     connect( The::engineController(), &EngineController::trackMetadataChanged,
0068              this, &MediaPlayer2Player::trackMetadataChanged );
0069     connect( The::engineController(), &EngineController::albumMetadataChanged,
0070              this, &MediaPlayer2Player::albumMetadataChanged );
0071     connect( The::engineController(), &EngineController::seekableChanged,
0072              this, &MediaPlayer2Player::seekableChanged );
0073     connect( The::engineController(), &EngineController::volumeChanged,
0074              this, &MediaPlayer2Player::volumeChanged );
0075     connect( The::engineController(), &EngineController::trackLengthChanged,
0076              this, &MediaPlayer2Player::trackLengthChanged );
0077     connect( The::engineController(), &EngineController::playbackStateChanged,
0078              this, &MediaPlayer2Player::playbackStateChanged );
0079     connect( The::playlistActions(),  &Playlist::Actions::navigatorChanged,
0080              this, &MediaPlayer2Player::playlistNavigatorChanged );
0081     connect( The::playlist()->qaim(), &QAbstractItemModel::rowsInserted,
0082              this, &MediaPlayer2Player::playlistRowsInserted );
0083     connect( The::playlist()->qaim(), &QAbstractItemModel::rowsMoved,
0084              this, &MediaPlayer2Player::playlistRowsMoved );
0085     connect( The::playlist()->qaim(), &QAbstractItemModel::rowsRemoved,
0086              this, &MediaPlayer2Player::playlistRowsRemoved );
0087     connect( The::playlist()->qaim(), &QAbstractItemModel::modelReset,
0088              this, &MediaPlayer2Player::playlistReplaced );
0089     connect( qobject_cast<Playlist::ProxyBase*>(The::playlist()->qaim()),
0090              &Playlist::ProxyBase::activeTrackChanged,
0091              this, &MediaPlayer2Player::playlistActiveTrackChanged );
0092 }
0093 
0094 MediaPlayer2Player::~MediaPlayer2Player()
0095 {
0096 }
0097 
0098 bool MediaPlayer2Player::CanGoNext() const
0099 {
0100     if ( AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatPlaylist )
0101     {
0102         return The::playlist()->qaim()->rowCount() > 0;
0103     }
0104     else
0105     {
0106         int activeRow = The::playlist()->activeRow();
0107         return activeRow < The::playlist()->qaim()->rowCount() - 1;
0108     }
0109 }
0110 
0111 void MediaPlayer2Player::Next() const
0112 {
0113     The::playlistActions()->next();
0114 }
0115 
0116 bool MediaPlayer2Player::CanGoPrevious() const
0117 {
0118     if ( AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatPlaylist )
0119     {
0120         return The::playlist()->qaim()->rowCount() > 0;
0121     }
0122     else
0123     {
0124         return The::playlist()->activeRow() > 0;
0125     }
0126 }
0127 
0128 void MediaPlayer2Player::Previous() const
0129 {
0130     The::playlistActions()->back();
0131 }
0132 
0133 bool MediaPlayer2Player::CanPause() const
0134 {
0135     return The::engineController()->currentTrack();
0136 }
0137 
0138 void MediaPlayer2Player::Pause() const
0139 {
0140     The::engineController()->pause();
0141 }
0142 
0143 void MediaPlayer2Player::PlayPause() const
0144 {
0145     The::engineController()->playPause();
0146 }
0147 
0148 void MediaPlayer2Player::Stop() const
0149 {
0150     The::engineController()->stop();
0151 }
0152 
0153 bool MediaPlayer2Player::CanPlay() const
0154 {
0155     return The::playlist()->qaim()->rowCount() > 0;
0156 }
0157 
0158 void MediaPlayer2Player::Play() const
0159 {
0160     The::engineController()->play();
0161 }
0162 
0163 void MediaPlayer2Player::SetPosition( const QDBusObjectPath& TrackId, qlonglong position ) const
0164 {
0165     QDBusObjectPath activeTrackId = activeMprisTrackId();
0166     if( TrackId == activeTrackId )
0167         The::engineController()->seekTo( position / 1000 );
0168     else
0169         debug() << "SetPosition() called with a trackId (" << TrackId.path() << ") which is not for the active track (" << activeTrackId.path() << ")";
0170 }
0171 
0172 void MediaPlayer2Player::OpenUri( const QString &Uri ) const
0173 {
0174     QUrl url( Uri );
0175     The::playlistController()->insertOptioned( url, Playlist::OnPlayMediaAction );
0176 }
0177 
0178 QString MediaPlayer2Player::PlaybackStatus() const
0179 {
0180     if( The::engineController()->isPlaying() )
0181         return QStringLiteral( "Playing" );
0182     else if ( The::engineController()->isPaused() )
0183         return QStringLiteral( "Paused" );
0184     else
0185         return QStringLiteral( "Stopped" );
0186 }
0187 
0188 QString MediaPlayer2Player::LoopStatus() const
0189 {
0190     switch( AmarokConfig::trackProgression() )
0191     {
0192         case AmarokConfig::EnumTrackProgression::Normal:
0193         case AmarokConfig::EnumTrackProgression::OnlyQueue:
0194         case AmarokConfig::EnumTrackProgression::RandomTrack:
0195         case AmarokConfig::EnumTrackProgression::RandomAlbum:
0196             return QStringLiteral( "None" );
0197         case AmarokConfig::EnumTrackProgression::RepeatTrack:
0198             return QStringLiteral( "Track" );
0199         case AmarokConfig::EnumTrackProgression::RepeatAlbum:
0200         case AmarokConfig::EnumTrackProgression::RepeatPlaylist:
0201             return QStringLiteral( "Playlist" );
0202         default:
0203             Q_ASSERT( false );
0204             return QStringLiteral( "None" );
0205     }
0206 }
0207 
0208 void MediaPlayer2Player::setLoopStatus( const QString& status ) const
0209 {
0210     AmarokConfig::EnumTrackProgression::type progression;
0211     if( status == QLatin1String( "None" ) )
0212         progression = AmarokConfig::EnumTrackProgression::Normal;
0213     else if( status == QLatin1String( "Track" ) )
0214         progression = AmarokConfig::EnumTrackProgression::RepeatTrack;
0215     else if( status == QLatin1String( "Playlist" ) )
0216         progression = AmarokConfig::EnumTrackProgression::RepeatPlaylist;
0217     else
0218     {
0219         debug() << "Unknown loop status:" << status;
0220         return;
0221     }
0222 
0223     AmarokConfig::setTrackProgression( progression );
0224     The::playlistActions()->playlistModeChanged();
0225 }
0226 
0227 double MediaPlayer2Player::Rate() const
0228 {
0229     return 1.0;
0230 }
0231 
0232 void MediaPlayer2Player::setRate( double rate ) const
0233 {
0234     Q_UNUSED(rate)
0235 }
0236 
0237 bool MediaPlayer2Player::Shuffle() const
0238 {
0239     switch( AmarokConfig::trackProgression() )
0240     {
0241         case AmarokConfig::EnumTrackProgression::RandomAlbum:
0242         case AmarokConfig::EnumTrackProgression::RandomTrack:
0243             return true;
0244         default:
0245             return false;
0246     }
0247 }
0248 
0249 void MediaPlayer2Player::setShuffle( bool shuffle ) const
0250 {
0251     AmarokConfig::EnumTrackProgression::type progression;
0252     if( shuffle )
0253         progression = AmarokConfig::EnumTrackProgression::RandomTrack;
0254     else
0255         progression = AmarokConfig::EnumTrackProgression::Normal;
0256 
0257     AmarokConfig::setTrackProgression( progression );
0258     The::playlistActions()->playlistModeChanged();
0259 }
0260 
0261 QVariantMap MediaPlayer2Player::metadataForTrack( const Meta::TrackPtr &track ) const
0262 {
0263     if ( !track )
0264         return QVariantMap();
0265 
0266     QVariantMap metaData = Meta::Field::mpris20MapFromTrack( track );
0267     if ( track == The::playlist()->activeTrack() )
0268         metaData[QStringLiteral("mpris:trackid")] = QVariant::fromValue<QDBusObjectPath>( activeMprisTrackId() );
0269     else {
0270         // we should be updated shortly
0271         QString path = QStringLiteral( "/org/kde/amarok/PendingTrack" );
0272         metaData[QStringLiteral("mpris:trackid")] = QVariant::fromValue<QDBusObjectPath>( QDBusObjectPath( path ) );
0273     }
0274     return metaData;
0275 }
0276 
0277 QVariantMap MediaPlayer2Player::Metadata() const
0278 {
0279     return metadataForTrack( The::engineController()->currentTrack() );
0280 }
0281 
0282 double MediaPlayer2Player::Volume() const
0283 {
0284     return static_cast<double>(The::engineController()->volume()) / 100.0;
0285 }
0286 
0287 void MediaPlayer2Player::setVolume( double volume ) const
0288 {
0289     if (volume < 0.0)
0290         volume = 0.0;
0291     if (volume > 1.0)
0292         volume = 1.0;
0293     The::engineController()->setVolume( volume * 100 );
0294 }
0295 
0296 qlonglong MediaPlayer2Player::Position() const
0297 {
0298     return The::engineController()->trackPositionMs() * 1000;
0299 }
0300 
0301 double MediaPlayer2Player::MinimumRate() const
0302 {
0303     return 1.0;
0304 }
0305 
0306 double MediaPlayer2Player::MaximumRate() const
0307 {
0308     return 1.0;
0309 }
0310 
0311 bool MediaPlayer2Player::CanSeek() const
0312 {
0313     return The::engineController()->isSeekable();
0314 }
0315 
0316 void MediaPlayer2Player::Seek( qlonglong Offset ) const
0317 {
0318     qlonglong offsetMs = Offset / 1000;
0319     int position = The::engineController()->trackPositionMs() + offsetMs;
0320     if( position < 0 )
0321         Previous();
0322     else if( position > The::engineController()->trackLength() )
0323         Next();
0324     else
0325         The::engineController()->seekTo( position );
0326 }
0327 
0328 bool MediaPlayer2Player::CanControl() const
0329 {
0330     return true;
0331 }
0332 
0333 void MediaPlayer2Player::trackPositionChanged( qint64 position, bool userSeek )
0334 {
0335     if ( userSeek )
0336         Q_EMIT Seeked( position * 1000 );
0337     m_lastPosition = position;
0338 }
0339 
0340 void MediaPlayer2Player::trackChanged( const Meta::TrackPtr &track )
0341 {
0342     signalPropertyChange( QStringLiteral("CanPause"), CanPause() );
0343     signalPropertyChange( QStringLiteral("Metadata"), metadataForTrack( track ) );
0344 }
0345 
0346 void MediaPlayer2Player::trackMetadataChanged( const Meta::TrackPtr &track )
0347 {
0348     signalPropertyChange( QStringLiteral("Metadata"), metadataForTrack( track ) );
0349 }
0350 
0351 void MediaPlayer2Player::albumMetadataChanged( const Meta::AlbumPtr &album )
0352 {
0353     Q_UNUSED( album )
0354     signalPropertyChange( QStringLiteral("Metadata"), Metadata() );
0355 }
0356 
0357 void MediaPlayer2Player::seekableChanged( bool seekable )
0358 {
0359     signalPropertyChange( QStringLiteral("CanSeek"), seekable );
0360 }
0361 
0362 void MediaPlayer2Player::volumeChanged( int percent )
0363 {
0364     DEBUG_BLOCK
0365     signalPropertyChange( QStringLiteral("Volume"), static_cast<double>(percent) / 100.0 );
0366 }
0367 
0368 void MediaPlayer2Player::trackLengthChanged( qint64 milliseconds )
0369 {
0370     // milliseconds < 0 is not a helpful value, and generally happens
0371     // when the track changes or playback is stopped; these cases are
0372     // dealt with better by other signal handlers
0373     if ( milliseconds >= 0 )
0374         signalPropertyChange( QStringLiteral("Metadata"), Metadata() );
0375 }
0376 
0377 void MediaPlayer2Player::playbackStateChanged()
0378 {
0379     signalPropertyChange( QStringLiteral("PlaybackStatus"), PlaybackStatus() );
0380 }
0381 
0382 void MediaPlayer2Player::playlistNavigatorChanged()
0383 {
0384     signalPropertyChange( QStringLiteral("CanGoNext"), CanGoNext() );
0385     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0386     signalPropertyChange( QStringLiteral("LoopStatus"), LoopStatus() );
0387     signalPropertyChange( QStringLiteral("Shuffle"), Shuffle() );
0388 }
0389 
0390 void MediaPlayer2Player::playlistRowsInserted( const QModelIndex&, int, int )
0391 {
0392     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0393     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0394 }
0395 
0396 void MediaPlayer2Player::playlistRowsMoved( const QModelIndex&, int, int, const QModelIndex&, int )
0397 {
0398     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0399     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0400 }
0401 
0402 void MediaPlayer2Player::playlistRowsRemoved( const QModelIndex&, int, int )
0403 {
0404     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0405     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0406 }
0407 
0408 void MediaPlayer2Player::playlistReplaced()
0409 {
0410     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0411     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0412 }
0413 
0414 void MediaPlayer2Player::playlistActiveTrackChanged( quint64 )
0415 {
0416     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0417     signalPropertyChange( QStringLiteral("CanGoPrevious"), CanGoPrevious() );
0418 }
0419 
0420