mediaplayer2player.cpp 8.14 KB
Newer Older
Michael Pyne's avatar
Michael Pyne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/***********************************************************************
 * Copyright 2012  Eike Hein <hein@kde.org>
 *
 * 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) version 3 or any later version
 * accepted by the membership of KDE e.V. (or its successor approved
 * by the membership of KDE e.V.), which shall act as a proxy
 * defined in Section 14 of version 3 of the license.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 ***********************************************************************/

#include "mpris2/mediaplayer2player.h"
#include "juk.h"
#include "playermanager.h"
24
#include "coverinfo.h"
25 26
#include "playlist.h"
#include "playlistitem.h"
Michael Pyne's avatar
Michael Pyne committed
27 28 29
#include "tag.h"
#include "filehandle.h"

30
#include <QCryptographicHash>
Michael Pyne's avatar
Michael Pyne committed
31 32
#include <QDBusConnection>
#include <QDBusMessage>
Michael Pyne's avatar
Michael Pyne committed
33
#include <QStandardPaths>
Michael Pyne's avatar
Michael Pyne committed
34 35
#include <QVariant>
#include <QFile>
Michael Pyne's avatar
Michael Pyne committed
36
#include <QUrl>
Michael Pyne's avatar
Michael Pyne committed
37

38
static QByteArray idFromPlaylistItem(const PlaylistItem *item)
39
{
40
    return QByteArray("/org/kde/juk/tid_") +
41
           QByteArray::number(item->trackId(), 16).rightJustified(8, '0');
42 43
}

Michael Pyne's avatar
Michael Pyne committed
44 45 46 47 48 49 50 51 52
MediaPlayer2Player::MediaPlayer2Player(QObject* parent)
    : QDBusAbstractAdaptor(parent)
    , m_player(JuK::JuKInstance()->playerManager())
{
    connect(m_player, SIGNAL(signalItemChanged(FileHandle)), this, SLOT(currentSourceChanged()));
    connect(m_player, SIGNAL(signalPlay()), this, SLOT(stateUpdated()));
    connect(m_player, SIGNAL(signalPause()), this, SLOT(stateUpdated()));
    connect(m_player, SIGNAL(signalStop()), this, SLOT(stateUpdated()));
    connect(m_player, SIGNAL(totalTimeChanged(int)), this, SLOT(totalTimeChanged()));
53 54 55
    connect(m_player, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool)));
    connect(m_player, SIGNAL(volumeChanged(float)), this, SLOT(volumeChanged(float)));
    connect(m_player, SIGNAL(seeked(int)), this, SLOT(seeked(int)));
Michael Pyne's avatar
Michael Pyne committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
}

MediaPlayer2Player::~MediaPlayer2Player()
{
}

bool MediaPlayer2Player::CanGoNext() const
{
    return true;
}

void MediaPlayer2Player::Next() const
{
    m_player->forward();
}

bool MediaPlayer2Player::CanGoPrevious() const
{
    return true;
}

void MediaPlayer2Player::Previous() const
{
    m_player->back();
}

bool MediaPlayer2Player::CanPause() const
{
84
    return true;
Michael Pyne's avatar
Michael Pyne committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
}

void MediaPlayer2Player::Pause() const
{
    m_player->pause();
}

void MediaPlayer2Player::PlayPause() const
{
    m_player->playPause();
}

void MediaPlayer2Player::Stop() const
{
    m_player->stop();
}

bool MediaPlayer2Player::CanPlay() const
{
    return true;
}

void MediaPlayer2Player::Play() const
{
    m_player->play();
}

void MediaPlayer2Player::SetPosition(const QDBusObjectPath& TrackId, qlonglong Position) const
{
114
    PlaylistItem *playingItem = Playlist::playingItem();
115

116
    if (!playingItem) {
117 118
        return;
    }
Michael Pyne's avatar
Michael Pyne committed
119

120
    // Verify the SetPosition call is against the currently playing track
121
    QByteArray currentTrackId = idFromPlaylistItem(playingItem);
122 123 124 125

    if (TrackId.path().toLatin1() == currentTrackId) {
        m_player->seek(Position / 1000);
    }
Michael Pyne's avatar
Michael Pyne committed
126 127 128 129
}

void MediaPlayer2Player::OpenUri(QString Uri) const
{
Michael Pyne's avatar
Michael Pyne committed
130
    QUrl url = QUrl::fromUserInput(Uri);
Michael Pyne's avatar
Michael Pyne committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

    // JuK does not yet support KIO
    if (url.isLocalFile()) {
        m_player->play(url.toLocalFile());
    }
}

QString MediaPlayer2Player::PlaybackStatus() const
{
    if (m_player->playing()) {
        return QLatin1String("Playing");
    }
    else if (m_player->paused()) {
        return QLatin1String("Paused");
    }

    return QLatin1String("Stopped");
}

QString MediaPlayer2Player::LoopStatus() const
{
    // TODO: Implement, although this is orthogonal to the PlayerManager
    return "None";
}

void MediaPlayer2Player::setLoopStatus(const QString& loopStatus) const
{
    Q_UNUSED(loopStatus)
}

double MediaPlayer2Player::Rate() const
{
    return 1.0;
}

void MediaPlayer2Player::setRate(double rate) const
{
    Q_UNUSED(rate)
}

bool MediaPlayer2Player::Shuffle() const
{
    // TODO: Implement
    return false;
}

void MediaPlayer2Player::setShuffle(bool shuffle) const
{
    Q_UNUSED(shuffle)
    // TODO: Implement
}

QVariantMap MediaPlayer2Player::Metadata() const
{
    QVariantMap metaData;

    // The track ID is annoying since it must result in a valid DBus object
    // path, and the regex for that is, and I quote: [a-zA-Z0-9_]*, along with
    // the normal / delimiters for paths.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    PlaylistItem *item = Playlist::playingItem();
    if (!item)
        return metaData;

    FileHandle playingFile = item->file();
    QByteArray playingTrackFileId = idFromPlaylistItem(item);

    metaData["mpris:trackid"] =
        QVariant::fromValue<QDBusObjectPath>(
                QDBusObjectPath(playingTrackFileId.constData()));

    metaData["xesam:album"] = playingFile.tag()->album();
    metaData["xesam:title"] = playingFile.tag()->title();
    metaData["xesam:artist"] = QStringList(playingFile.tag()->artist());
    metaData["xesam:genre"]  = QStringList(playingFile.tag()->genre());

    metaData["mpris:length"] = qint64(playingFile.tag()->seconds() * 1000000);
Michael Pyne's avatar
Michael Pyne committed
207 208
    metaData["xesam:url"] = QString::fromUtf8(
            QUrl::fromLocalFile(playingFile.absFilePath()).toEncoded());
209

210
    if(playingFile.coverInfo()->hasCover()) {
Michael Pyne's avatar
Michael Pyne committed
211
        QString fallbackFileName = QStandardPaths::locate(QStandardPaths::TempLocation,
212 213 214 215 216 217 218
                QString("juk-cover-%1.png").arg(item->trackId()));

        QString path = fallbackFileName;
        if(!QFile::exists(path)) {
            path = playingFile.coverInfo()->localPathToCover(fallbackFileName);
        }

Michael Pyne's avatar
Michael Pyne committed
219 220
        metaData["mpris:artUrl"] = QString::fromUtf8(
                QUrl::fromLocalFile(path).toEncoded());
221
    }
Michael Pyne's avatar
Michael Pyne committed
222 223 224 225 226 227 228 229 230 231 232

    return metaData;
}

double MediaPlayer2Player::Volume() const
{
    return m_player->volume();
}

void MediaPlayer2Player::setVolume(double volume) const
{
233 234 235 236
    if (volume < 0.0)
        volume = 0.0;
    if (volume > 1.0)
        volume = 1.0;
Michael Pyne's avatar
Michael Pyne committed
237 238 239 240 241
    m_player->setVolume(volume);
}

qlonglong MediaPlayer2Player::Position() const
{
242
    return m_player->currentTimeMSecs() * 1000;
Michael Pyne's avatar
Michael Pyne committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
}

double MediaPlayer2Player::MinimumRate() const
{
    return 1.0;
}

double MediaPlayer2Player::MaximumRate() const
{
    return 1.0;
}

bool MediaPlayer2Player::CanSeek() const
{
    return m_player->seekable();
}

void MediaPlayer2Player::Seek(qlonglong Offset) const
{
262
    m_player->seek(((m_player->currentTimeMSecs() * 1000) + Offset) / 1000);
Michael Pyne's avatar
Michael Pyne committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
}

bool MediaPlayer2Player::CanControl() const
{
    return true;
}

void MediaPlayer2Player::currentSourceChanged() const
{
    QVariantMap properties;
    properties["Metadata"] = Metadata();
    properties["CanSeek"] = CanSeek();
    signalPropertiesChange(properties);
}

void MediaPlayer2Player::stateUpdated() const
{
    QVariantMap properties;
    properties["PlaybackStatus"] = PlaybackStatus();
    signalPropertiesChange(properties);
}

void MediaPlayer2Player::totalTimeChanged() const
{
    QVariantMap properties;
    properties["Metadata"] = Metadata();
    signalPropertiesChange(properties);
}

void MediaPlayer2Player::seekableChanged(bool seekable) const
{
    QVariantMap properties;
    properties["CanSeek"] = seekable;
    signalPropertiesChange(properties);
}

299
void MediaPlayer2Player::volumeChanged(float newVol) const
Michael Pyne's avatar
Michael Pyne committed
300 301 302 303 304 305 306 307
{
    Q_UNUSED(newVol)

    QVariantMap properties;
    properties["Volume"] = Volume();
    signalPropertiesChange(properties);
}

308 309 310 311 312 313
void MediaPlayer2Player::seeked(int newPos) const
{
    // casts int to uint64
    emit Seeked(newPos);
}

Michael Pyne's avatar
Michael Pyne committed
314 315 316 317 318
void MediaPlayer2Player::signalPropertiesChange(const QVariantMap& properties) const
{
    QDBusMessage msg = QDBusMessage::createSignal("/org/mpris/MediaPlayer2",
        "org.freedesktop.DBus.Properties", "PropertiesChanged" );

319 320 321
    msg << "org.mpris.MediaPlayer2.Player";
    msg << properties;
    msg << QStringList();
Michael Pyne's avatar
Michael Pyne committed
322 323 324

    QDBusConnection::sessionBus().send(msg);
}