Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9948bf3c3 | ||
|
|
94c3e653f7 |
No files matched your search
+1
-1
@@ -33,7 +33,7 @@ ecm_setup_version(${PROJECT_VERSION}
|
||||
|
||||
kde_enable_exceptions()
|
||||
|
||||
find_package(Qt5 ${QT5_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Sql)
|
||||
find_package(Qt5 ${QT5_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Sql DBus)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n CoreAddons Crash)
|
||||
find_package(pybind11 REQUIRED)
|
||||
find_package(Ytdlp REQUIRED RUNTIME)
|
||||
|
||||
@@ -19,6 +19,7 @@ target_include_directories(asyncdatabase PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(asyncdatabase PUBLIC Qt5::Sql)
|
||||
|
||||
add_subdirectory(example)
|
||||
add_subdirectory(mpris2)
|
||||
|
||||
add_executable(audiotube
|
||||
main.cpp
|
||||
@@ -50,6 +51,7 @@ target_link_libraries(audiotube
|
||||
KF5::Crash
|
||||
ytm
|
||||
asyncdatabase
|
||||
mpris2
|
||||
)
|
||||
|
||||
target_compile_definitions(audiotube PRIVATE -DQT_NO_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING)
|
||||
|
||||
@@ -98,7 +98,17 @@ Flickable {
|
||||
videoId: UserPlaylistModel.currentVideoId
|
||||
onTitleChanged: Library.addPlaybackHistoryItem(info.videoId, info.title)
|
||||
}
|
||||
|
||||
|
||||
MprisPlayer {
|
||||
id: mpris
|
||||
|
||||
trackTitle: info.title
|
||||
album: "Test Album"
|
||||
playbackState: MprisPlayer.PlaybackRunning
|
||||
playerName: "AudioTube"
|
||||
trackDuration: audio.duration
|
||||
}
|
||||
|
||||
property var audioPlayer: Audio {
|
||||
id: audio
|
||||
|
||||
|
||||
+4
-1
@@ -11,6 +11,8 @@
|
||||
#include <KCrash>
|
||||
#include <KAboutData>
|
||||
|
||||
#include "declarativemprisplayer.h"
|
||||
#include "mpris2.h"
|
||||
#include "searchmodel.h"
|
||||
#include "albummodel.h"
|
||||
#include "videoinfoextractor.h"
|
||||
@@ -21,7 +23,7 @@
|
||||
#include "playerutils.h"
|
||||
#include "library.h"
|
||||
|
||||
#include "asyncdatabase.h"
|
||||
#include <mediaplayer2.h>
|
||||
|
||||
constexpr auto URI = "org.kde.ytmusic";
|
||||
|
||||
@@ -45,6 +47,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
qmlRegisterType<VideoInfoExtractor>(URI, 1, 0, "VideoInfoExtractor");
|
||||
qmlRegisterType<ArtistModel>(URI, 1, 0, "ArtistModel");
|
||||
qmlRegisterType<PlaylistModel>(URI, 1, 0, "PlaylistModel");
|
||||
qmlRegisterType<DeclarativeMprisPlayer>(URI, 1, 0, "MprisPlayer");
|
||||
qmlRegisterSingletonType<UserPlaylistModel>(URI, 1, 0, "UserPlaylistModel", [](QQmlEngine *, QJSEngine *) {
|
||||
return new UserPlaylistModel();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
add_library(mpris2 STATIC
|
||||
mediaplayer2.cpp
|
||||
mediaplayer2.h
|
||||
mediaplayer2player.cpp
|
||||
mediaplayer2player.h
|
||||
mpris2.cpp
|
||||
mpris2.h
|
||||
declarativemprisplayer.cpp
|
||||
declarativemprisplayer.h
|
||||
)
|
||||
|
||||
target_link_libraries(mpris2 PUBLIC Qt::Core Qt::Gui Qt::DBus KF5::CoreAddons)
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "declarativemprisplayer.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
DeclarativeMprisPlayer::DeclarativeMprisPlayer()
|
||||
{
|
||||
QTimer::singleShot(0, [this]() {
|
||||
connect(m_mpris.player(), &MediaPlayer2Player::play, this, &DeclarativeMprisPlayer::play);
|
||||
connect(m_mpris.player(), &MediaPlayer2Player::playPause, this, &DeclarativeMprisPlayer::playPause);
|
||||
connect(m_mpris.player(), &MediaPlayer2Player::Stop, this, &DeclarativeMprisPlayer::stop);
|
||||
});
|
||||
}
|
||||
|
||||
QString DeclarativeMprisPlayer::playerName() const {
|
||||
return m_mpris.playerName();
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setPlayerName(const QString &name) {
|
||||
m_mpris.setPlayerName(name);
|
||||
Q_EMIT playerNameChanged();
|
||||
}
|
||||
|
||||
DeclarativeMprisPlayer::PlaybackState DeclarativeMprisPlayer::playbackState() {
|
||||
return PlaybackState(m_mpris.player()->playbackState());
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setPlaybackState(PlaybackState state) {
|
||||
m_mpris.player()->setPlaybackState(MediaPlayer2Player::PlaybackState(state));
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setAlbum(const QString &album) {
|
||||
m_mpris.player()->setCurrentTrackAlbum(album);
|
||||
}
|
||||
|
||||
QString DeclarativeMprisPlayer::album() const {
|
||||
return m_mpris.player()->currentTrackAlbum();
|
||||
}
|
||||
|
||||
QUrl DeclarativeMprisPlayer::albumArt() const {
|
||||
return m_mpris.player()->currentTrackCoverArt();
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setAlbumArt(const QUrl &url) {
|
||||
m_mpris.player()->setCurrentTrackCoverArt(url);
|
||||
}
|
||||
|
||||
QString DeclarativeMprisPlayer::trackTitle() const
|
||||
{
|
||||
return m_mpris.player()->currentTrackTitle();
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setTrackTitle(const QString &title)
|
||||
{
|
||||
m_mpris.player()->setCurrentTrackTitle(title);
|
||||
}
|
||||
|
||||
void DeclarativeMprisPlayer::setTrackDuration(qlonglong trackDuration)
|
||||
{
|
||||
return m_mpris.player()->setDuration(trackDuration);
|
||||
}
|
||||
qlonglong DeclarativeMprisPlayer::trackDuration() const { return 0; }
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "mediaplayer2player.h"
|
||||
#include "mpris2.h"
|
||||
|
||||
|
||||
class DeclarativeMprisPlayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString playerName READ playerName WRITE setPlayerName NOTIFY playerNameChanged)
|
||||
Q_PROPERTY(PlaybackState playbackState READ playbackState WRITE setPlaybackState NOTIFY playbackStateChanged)
|
||||
Q_PROPERTY(QString album READ album WRITE setAlbum NOTIFY albumChanged)
|
||||
Q_PROPERTY(QUrl albumArt READ albumArt WRITE setAlbumArt NOTIFY albumArtChanged)
|
||||
Q_PROPERTY(QString trackTitle READ trackTitle WRITE setTrackTitle NOTIFY trackTitleChanged);
|
||||
Q_PROPERTY(qlonglong trackDuration READ trackDuration WRITE setTrackDuration NOTIFY trackDurationChanged);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
enum PlaybackState {
|
||||
PlaybackStopped = MediaPlayer2Player::PlaybackStopped,
|
||||
PlaybackRunning = MediaPlayer2Player::PlaybackRunning,
|
||||
PlaybackPaused = MediaPlayer2Player::PlaybackPaused
|
||||
};
|
||||
Q_ENUM(PlaybackState)
|
||||
|
||||
DeclarativeMprisPlayer();
|
||||
|
||||
QString playerName() const;
|
||||
void setPlayerName(const QString &name);
|
||||
Q_SIGNAL void playerNameChanged();
|
||||
|
||||
PlaybackState playbackState();
|
||||
void setPlaybackState(PlaybackState state);
|
||||
Q_SIGNAL void playbackStateChanged();
|
||||
|
||||
void setAlbum(const QString &album);
|
||||
QString album() const;
|
||||
Q_SIGNAL void albumChanged();
|
||||
|
||||
QUrl albumArt() const;
|
||||
void setAlbumArt(const QUrl &url);
|
||||
Q_SIGNAL void albumArtChanged();
|
||||
|
||||
QString trackTitle() const;
|
||||
void setTrackTitle(const QString &title);
|
||||
Q_SIGNAL void trackTitleChanged();
|
||||
|
||||
qlonglong trackDuration() const;
|
||||
void setTrackDuration(qlonglong trackDuration);
|
||||
Q_SIGNAL void trackDurationChanged();
|
||||
|
||||
Q_SIGNAL void changeVolume();
|
||||
Q_SIGNAL void play();
|
||||
Q_SIGNAL void playPause();
|
||||
Q_SIGNAL void stop();
|
||||
Q_SIGNAL void goPrevious();
|
||||
Q_SIGNAL void goNext();
|
||||
|
||||
private:
|
||||
Mpris2 m_mpris;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "mediaplayer2.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <KAboutData>
|
||||
|
||||
MediaPlayer2::MediaPlayer2(QObject* parent)
|
||||
: QDBusAbstractAdaptor(parent)
|
||||
{
|
||||
}
|
||||
|
||||
MediaPlayer2::~MediaPlayer2()
|
||||
= default;
|
||||
|
||||
bool MediaPlayer2::CanQuit() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MediaPlayer2::CanRaise() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool MediaPlayer2::HasTrackList() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void MediaPlayer2::Quit()
|
||||
{
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
void MediaPlayer2::Raise()
|
||||
{
|
||||
Q_EMIT raisePlayer();
|
||||
}
|
||||
|
||||
QString MediaPlayer2::Identity() const
|
||||
{
|
||||
return KAboutData::applicationData().displayName();
|
||||
}
|
||||
|
||||
QString MediaPlayer2::DesktopEntry() const
|
||||
{
|
||||
return KAboutData::applicationData().desktopFileName();
|
||||
}
|
||||
|
||||
QStringList MediaPlayer2::SupportedUriSchemes() const
|
||||
{
|
||||
return QStringList() << QStringLiteral("file");
|
||||
}
|
||||
|
||||
QStringList MediaPlayer2::SupportedMimeTypes() const
|
||||
{
|
||||
// KService::Ptr app = KService::serviceByDesktopName(KCmdLineArgs::aboutData()->appName());
|
||||
|
||||
// if (app) {
|
||||
// return app->mimeTypes();
|
||||
// }
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
#include "moc_mediaplayer2.cpp"
|
||||
@@ -0,0 +1,51 @@
|
||||
/***************************************************************************
|
||||
SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDBusAbstractAdaptor>
|
||||
#include <QStringList>
|
||||
|
||||
|
||||
class MediaPlayer2 : public QDBusAbstractAdaptor
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2") // Docs: https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html
|
||||
|
||||
Q_PROPERTY(bool CanQuit READ CanQuit CONSTANT)
|
||||
Q_PROPERTY(bool CanRaise READ CanRaise CONSTANT)
|
||||
Q_PROPERTY(bool HasTrackList READ HasTrackList CONSTANT)
|
||||
|
||||
Q_PROPERTY(QString Identity READ Identity CONSTANT)
|
||||
Q_PROPERTY(QString DesktopEntry READ DesktopEntry CONSTANT)
|
||||
|
||||
Q_PROPERTY(QStringList SupportedUriSchemes READ SupportedUriSchemes CONSTANT)
|
||||
Q_PROPERTY(QStringList SupportedMimeTypes READ SupportedMimeTypes CONSTANT)
|
||||
|
||||
public:
|
||||
explicit MediaPlayer2(QObject* parent = nullptr);
|
||||
~MediaPlayer2() override;
|
||||
|
||||
[[nodiscard]] bool CanQuit() const;
|
||||
[[nodiscard]] bool CanRaise() const;
|
||||
[[nodiscard]] bool HasTrackList() const;
|
||||
|
||||
[[nodiscard]] QString Identity() const;
|
||||
[[nodiscard]] QString DesktopEntry() const;
|
||||
|
||||
[[nodiscard]] QStringList SupportedUriSchemes() const;
|
||||
[[nodiscard]] QStringList SupportedMimeTypes() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void Quit();
|
||||
void Raise();
|
||||
|
||||
Q_SIGNALS:
|
||||
void raisePlayer();
|
||||
|
||||
};
|
||||
@@ -0,0 +1,383 @@
|
||||
// SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2022 (c) Jonah Brüchert <[email protected]>
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "mediaplayer2player.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QStringBuilder>
|
||||
#include <QDBusConnection>
|
||||
#include <QMetaClassInfo>
|
||||
|
||||
#include <QImage>
|
||||
#include <QBuffer>
|
||||
|
||||
|
||||
constexpr double MAX_RATE = 1.0;
|
||||
constexpr const double MIN_RATE = 1.0;
|
||||
|
||||
MediaPlayer2Player::MediaPlayer2Player(QObject* parent)
|
||||
: QDBusAbstractAdaptor(parent)
|
||||
{
|
||||
m_volume = 0; // TODO
|
||||
m_canPlay = true; // TODO
|
||||
signalPropertiesChange(QStringLiteral("Volume"), Volume());
|
||||
|
||||
m_mediaPlayerPresent = 1;
|
||||
}
|
||||
|
||||
MediaPlayer2Player::~MediaPlayer2Player() = default;
|
||||
|
||||
QString MediaPlayer2Player::PlaybackStatus() const
|
||||
{
|
||||
switch (m_playbackState) {
|
||||
case PlaybackStopped:
|
||||
return QStringLiteral("Stopped");
|
||||
case PlaybackRunning:
|
||||
return QStringLiteral("Playing");
|
||||
case PlaybackPaused:
|
||||
return QStringLiteral("Paused");
|
||||
}
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanGoNext() const
|
||||
{
|
||||
return m_canGoNext;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Next()
|
||||
{
|
||||
Q_EMIT next();
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanGoPrevious() const
|
||||
{
|
||||
return m_canGoPrevious;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Previous()
|
||||
{
|
||||
Q_EMIT previous();
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanPause() const
|
||||
{
|
||||
return m_canPlay;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Pause()
|
||||
{
|
||||
Q_EMIT pause();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::PlayPause()
|
||||
{
|
||||
Q_EMIT playPause();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Stop()
|
||||
{
|
||||
Q_EMIT stop();
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanPlay() const
|
||||
{
|
||||
return m_canPlay;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Play()
|
||||
{
|
||||
Q_EMIT play();
|
||||
}
|
||||
|
||||
double MediaPlayer2Player::Volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setVolume(double volume)
|
||||
{
|
||||
m_volume = qBound(0.0, volume, 1.0);
|
||||
Q_EMIT volumeChanged(m_volume);
|
||||
|
||||
signalPropertiesChange(QStringLiteral("Volume"), Volume());
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setPropertyPosition(int newPositionInMs)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
QVariantMap MediaPlayer2Player::Metadata() const
|
||||
{
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
qlonglong MediaPlayer2Player::Position() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
double MediaPlayer2Player::Rate() const
|
||||
{
|
||||
return m_rate;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setRate(double newRate)
|
||||
{
|
||||
if (newRate <= 0.0001 && newRate >= -0.0001) {
|
||||
Pause();
|
||||
} else {
|
||||
m_rate = qBound(MinimumRate(), newRate, MaximumRate());
|
||||
Q_EMIT rateChanged(m_rate);
|
||||
|
||||
signalPropertiesChange(QStringLiteral("Rate"), Rate());
|
||||
}
|
||||
}
|
||||
|
||||
double MediaPlayer2Player::MinimumRate() const
|
||||
{
|
||||
return MIN_RATE;
|
||||
}
|
||||
|
||||
double MediaPlayer2Player::MaximumRate() const
|
||||
{
|
||||
return MAX_RATE;
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanSeek() const
|
||||
{
|
||||
return m_playerIsSeekableChanged;
|
||||
}
|
||||
|
||||
bool MediaPlayer2Player::CanControl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::Seek(qlonglong Offset)
|
||||
{
|
||||
Q_EMIT seek(Offset);
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::SetPosition(const QDBusObjectPath &trackId, qlonglong pos)
|
||||
{
|
||||
Q_EMIT positionRequested(pos);
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::OpenUri(const QString &uri)
|
||||
{
|
||||
Q_UNUSED(uri);
|
||||
}
|
||||
|
||||
//void MediaPlayer2Player::playerSourceChanged()
|
||||
//{
|
||||
// setCurrentTrack(m_manageAudioPlayer->playListPosition());
|
||||
//}
|
||||
|
||||
void MediaPlayer2Player::playControlEnabledChanged()
|
||||
{
|
||||
signalPropertiesChange(QStringLiteral("CanPause"), CanPause());
|
||||
signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay());
|
||||
|
||||
Q_EMIT canPauseChanged();
|
||||
Q_EMIT canPlayChanged();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::skipBackwardControlEnabledChanged()
|
||||
{
|
||||
signalPropertiesChange(QStringLiteral("CanGoPrevious"), CanGoPrevious());
|
||||
Q_EMIT canGoPreviousChanged();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::skipForwardControlEnabledChanged()
|
||||
{
|
||||
signalPropertiesChange(QStringLiteral("CanGoNext"), CanGoNext());
|
||||
Q_EMIT canGoNextChanged();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::playerPlaybackStateChanged()
|
||||
{
|
||||
signalPropertiesChange(QStringLiteral("PlaybackStatus"), PlaybackStatus());
|
||||
Q_EMIT playbackStatusChanged();
|
||||
|
||||
playerIsSeekableChanged();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::playerIsSeekableChanged()
|
||||
{
|
||||
signalPropertiesChange(QStringLiteral("CanSeek"), CanSeek());
|
||||
Q_EMIT canSeekChanged();
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::audioPositionChanged()
|
||||
{
|
||||
setPropertyPosition(m_position);
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::playerSeeked(qint64 position)
|
||||
{
|
||||
Q_EMIT Seeked(position * 1000);
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::audioDurationChanged()
|
||||
{
|
||||
m_metadata = getMetadataOfCurrentTrack();
|
||||
signalPropertiesChange(QStringLiteral("Metadata"), Metadata());
|
||||
|
||||
skipBackwardControlEnabledChanged();
|
||||
skipForwardControlEnabledChanged();
|
||||
playerPlaybackStateChanged();
|
||||
playerIsSeekableChanged();
|
||||
setPropertyPosition(m_position);
|
||||
}
|
||||
|
||||
QVariantMap MediaPlayer2Player::getMetadataOfCurrentTrack()
|
||||
{
|
||||
auto result = QVariantMap();
|
||||
|
||||
if (m_currentTrackId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
result[QStringLiteral("mpris:trackid")] = QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath(m_currentTrackId));
|
||||
result[QStringLiteral("mpris:length")] = m_duration * 1000;
|
||||
//convert milli-seconds into micro-seconds
|
||||
result[QStringLiteral("xesam:title")] = m_currentTrackTitle;
|
||||
result[QStringLiteral("xesam:url")] = m_currentTrackUrl;
|
||||
result[QStringLiteral("xesam:album")] = m_currentTrackAlbum;
|
||||
result[QStringLiteral("xesam:artist")] = m_currentTrackArtist;
|
||||
result[QStringLiteral("mpris:artUrl")] = m_coverArtUrl;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int MediaPlayer2Player::mediaPlayerPresent() const
|
||||
{
|
||||
return m_mediaPlayerPresent;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setDuration(qlonglong duration) {
|
||||
m_duration = duration;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setPlaybackState(PlaybackState newState)
|
||||
{
|
||||
m_playbackState = newState;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackTitle(const QString &title)
|
||||
{
|
||||
m_currentTrackTitle = title;
|
||||
}
|
||||
|
||||
QString MediaPlayer2Player::currentTrackTitle() const
|
||||
{
|
||||
return m_currentTrackTitle;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackUrl(const QUrl &url)
|
||||
{
|
||||
m_currentTrackUrl = url;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackAlbum(const QString &album)
|
||||
{
|
||||
m_currentTrackAlbum = album;
|
||||
}
|
||||
|
||||
QString MediaPlayer2Player::currentTrackAlbum() const
|
||||
{
|
||||
return m_currentTrackAlbum;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackArtist(const QString &artist)
|
||||
{
|
||||
m_currentTrackArtist = artist;
|
||||
}
|
||||
|
||||
QString MediaPlayer2Player::currentTrackArtist() const
|
||||
{
|
||||
return m_currentTrackArtist;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackCoverArt(const QUrl &url)
|
||||
{
|
||||
m_coverArtUrl = url;
|
||||
}
|
||||
|
||||
QUrl MediaPlayer2Player::currentTrackCoverArt() const
|
||||
{
|
||||
return m_coverArtUrl;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCurrentTrackCoverArt(const QImage &image)
|
||||
{
|
||||
QByteArray data;
|
||||
QBuffer buffer(&data);
|
||||
image.save(&buffer);
|
||||
m_coverArtUrl = QUrl(QString("data:image/png;base64," % data));
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setCanGoPrevious(bool value)
|
||||
{
|
||||
m_canGoPrevious = value;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::canGoForward(bool value)
|
||||
{
|
||||
m_canGoNext = value;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::canPause(bool value)
|
||||
{
|
||||
m_canPause = value;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::canPlay(bool value)
|
||||
{
|
||||
m_canPlay = value;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setPosition(qlonglong position) {
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::setMediaPlayerPresent(int status)
|
||||
{
|
||||
if (m_mediaPlayerPresent != status) {
|
||||
m_mediaPlayerPresent = status;
|
||||
Q_EMIT mediaPlayerPresentChanged();
|
||||
|
||||
signalPropertiesChange(QStringLiteral("CanGoNext"), CanGoNext());
|
||||
signalPropertiesChange(QStringLiteral("CanGoPrevious"), CanGoPrevious());
|
||||
signalPropertiesChange(QStringLiteral("CanPause"), CanPause());
|
||||
signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay());
|
||||
Q_EMIT canGoNextChanged();
|
||||
Q_EMIT canGoPreviousChanged();
|
||||
Q_EMIT canPauseChanged();
|
||||
Q_EMIT canPlayChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPlayer2Player::signalPropertiesChange(const QString &property, const QVariant &value)
|
||||
{
|
||||
QVariantMap properties;
|
||||
properties[property] = value;
|
||||
const int ifaceIndex = metaObject()->indexOfClassInfo("D-Bus Interface");
|
||||
QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/mpris/MediaPlayer2"),
|
||||
QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"));
|
||||
|
||||
msg << QLatin1String(metaObject()->classInfo(ifaceIndex).value());
|
||||
msg << properties;
|
||||
msg << QStringList();
|
||||
|
||||
QDBusConnection::sessionBus().send(msg);
|
||||
}
|
||||
|
||||
#include "moc_mediaplayer2player.cpp"
|
||||
@@ -0,0 +1,244 @@
|
||||
// SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2022 (c) Jonah Brüchert <[email protected]>
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDBusAbstractAdaptor>
|
||||
#include <QDBusObjectPath>
|
||||
#include <QDBusMessage>
|
||||
#include <QUrl>
|
||||
|
||||
class MediaPlayListProxyModel;
|
||||
class ManageAudioPlayer;
|
||||
class ManageMediaPlayerControl;
|
||||
class ManageHeaderBar;
|
||||
class AudioWrapper;
|
||||
|
||||
class MediaPlayer2Player : public QDBusAbstractAdaptor
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player") // Docs: https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
|
||||
|
||||
Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus NOTIFY playbackStatusChanged)
|
||||
Q_PROPERTY(double Rate READ Rate WRITE setRate NOTIFY rateChanged)
|
||||
Q_PROPERTY(QVariantMap Metadata READ Metadata NOTIFY playbackStatusChanged)
|
||||
Q_PROPERTY(double Volume READ Volume WRITE setVolume NOTIFY volumeChanged)
|
||||
Q_PROPERTY(qlonglong Position READ Position WRITE setPropertyPosition NOTIFY playbackStatusChanged)
|
||||
Q_PROPERTY(double MinimumRate READ MinimumRate CONSTANT)
|
||||
Q_PROPERTY(double MaximumRate READ MaximumRate CONSTANT)
|
||||
Q_PROPERTY(bool CanGoNext READ CanGoNext NOTIFY canGoNextChanged)
|
||||
Q_PROPERTY(bool CanGoPrevious READ CanGoPrevious NOTIFY canGoPreviousChanged)
|
||||
Q_PROPERTY(bool CanPlay READ CanPlay NOTIFY canPlayChanged)
|
||||
Q_PROPERTY(bool CanPause READ CanPause NOTIFY canPauseChanged)
|
||||
Q_PROPERTY(bool CanControl READ CanControl NOTIFY canControlChanged)
|
||||
Q_PROPERTY(bool CanSeek READ CanSeek NOTIFY canSeekChanged)
|
||||
Q_PROPERTY(int mediaPlayerPresent READ mediaPlayerPresent WRITE setMediaPlayerPresent NOTIFY mediaPlayerPresentChanged)
|
||||
|
||||
public:
|
||||
explicit MediaPlayer2Player(QObject* parent = nullptr);
|
||||
~MediaPlayer2Player() override;
|
||||
|
||||
QString PlaybackStatus() const;
|
||||
double Rate() const;
|
||||
QVariantMap Metadata() const;
|
||||
double Volume() const;
|
||||
qlonglong Position() const;
|
||||
double MinimumRate() const;
|
||||
double MaximumRate() const;
|
||||
bool CanGoNext() const;
|
||||
bool CanGoPrevious() const;
|
||||
bool CanPlay() const;
|
||||
bool CanPause() const;
|
||||
bool CanSeek() const;
|
||||
bool CanControl() const;
|
||||
int currentTrack() const;
|
||||
int mediaPlayerPresent() const;
|
||||
|
||||
void setDesktopFileName(const QString &fileName);
|
||||
|
||||
///
|
||||
/// Signal to the mpris interface that the playback is now at a new position
|
||||
///
|
||||
void setPosition(qlonglong pos);
|
||||
|
||||
///
|
||||
/// Signal the length of the current track to the mpris interface
|
||||
///
|
||||
void setDuration(qlonglong duration);
|
||||
|
||||
enum PlaybackState {
|
||||
PlaybackStopped,
|
||||
PlaybackRunning,
|
||||
PlaybackPaused
|
||||
};
|
||||
|
||||
///
|
||||
/// Set the current playback state
|
||||
///
|
||||
void setPlaybackState(PlaybackState newState);
|
||||
PlaybackState playbackState() const {
|
||||
return m_playbackState;
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the current track's title
|
||||
///
|
||||
void setCurrentTrackTitle(const QString &title);
|
||||
QString currentTrackTitle() const;
|
||||
|
||||
///
|
||||
/// Set the current track's URL
|
||||
///
|
||||
void setCurrentTrackUrl(const QUrl &url);
|
||||
|
||||
///
|
||||
/// Set the album of the currently playing track
|
||||
///
|
||||
void setCurrentTrackAlbum(const QString &album);
|
||||
QString currentTrackAlbum() const;
|
||||
|
||||
///
|
||||
/// Set current track's artist
|
||||
///
|
||||
void setCurrentTrackArtist(const QString &artist);
|
||||
QString currentTrackArtist() const;
|
||||
|
||||
///
|
||||
/// Set cover art url
|
||||
///
|
||||
void setCurrentTrackCoverArt(const QUrl &url);
|
||||
QUrl currentTrackCoverArt() const;
|
||||
|
||||
///
|
||||
/// Set cover art image
|
||||
///
|
||||
void setCurrentTrackCoverArt(const QImage &image);
|
||||
|
||||
void setCanGoPrevious(bool);
|
||||
void canGoForward(bool);
|
||||
void canPause(bool value);
|
||||
void canPlay(bool value);
|
||||
|
||||
Q_SIGNALS:
|
||||
void Seeked(qlonglong Position);
|
||||
|
||||
void rateChanged(double newRate);
|
||||
|
||||
///
|
||||
/// Emitted when the volume should be changed to newVol
|
||||
///
|
||||
void volumeChanged(double newVol);
|
||||
void playbackStatusChanged();
|
||||
void canGoNextChanged();
|
||||
void canGoPreviousChanged();
|
||||
void canPlayChanged();
|
||||
void canPauseChanged();
|
||||
void canControlChanged();
|
||||
void canSeekChanged();
|
||||
void currentTrackChanged();
|
||||
void mediaPlayerPresentChanged();
|
||||
|
||||
///
|
||||
/// Emitted when the player should skip to the next track
|
||||
///
|
||||
void next();
|
||||
|
||||
///
|
||||
/// Emitted when it is requested to skip to the previous track
|
||||
/// over the MPRIS interface
|
||||
///
|
||||
void previous();
|
||||
|
||||
///
|
||||
/// Emitted when play / pause should be toggled
|
||||
///
|
||||
void playPause();
|
||||
|
||||
///
|
||||
/// Emitted when the playback should be stopped
|
||||
///
|
||||
void stop();
|
||||
|
||||
///
|
||||
/// Emitted when playback should be resumed
|
||||
///
|
||||
void play();
|
||||
|
||||
///
|
||||
/// Emitted when the player should seek
|
||||
///
|
||||
void seek(qlonglong offset);
|
||||
|
||||
///
|
||||
/// Emitted when the player should skip to the given position
|
||||
///
|
||||
void positionRequested(qlonglong position);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
void Next();
|
||||
void Previous();
|
||||
void Pause();
|
||||
void PlayPause();
|
||||
void Stop();
|
||||
void Play();
|
||||
void Seek(qlonglong Offset);
|
||||
void SetPosition(const QDBusObjectPath &trackId, qlonglong pos);
|
||||
void OpenUri(const QString &uri);
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void playControlEnabledChanged();
|
||||
|
||||
void skipBackwardControlEnabledChanged();
|
||||
|
||||
void skipForwardControlEnabledChanged();
|
||||
|
||||
void playerPlaybackStateChanged();
|
||||
|
||||
void playerIsSeekableChanged();
|
||||
|
||||
void audioPositionChanged();
|
||||
|
||||
void playerSeeked(qint64 position);
|
||||
|
||||
void audioDurationChanged();
|
||||
|
||||
private:
|
||||
void signalPropertiesChange(const QString &property, const QVariant &value);
|
||||
|
||||
void setMediaPlayerPresent(int status);
|
||||
void setRate(double newRate);
|
||||
void setVolume(double volume);
|
||||
void setPropertyPosition(int newPositionInMs);
|
||||
|
||||
QVariantMap getMetadataOfCurrentTrack();
|
||||
|
||||
QVariantMap m_metadata;
|
||||
QString m_currentTrack;
|
||||
QString m_currentTrackId;
|
||||
double m_rate = 1.0;
|
||||
double m_volume = 0.0;
|
||||
int m_mediaPlayerPresent = 0;
|
||||
bool m_canPause = true;
|
||||
bool m_canPlay = false;
|
||||
bool m_canGoNext = false;
|
||||
bool m_canGoPrevious = false;
|
||||
qlonglong m_position = 0;
|
||||
bool m_playerIsSeekableChanged = false;
|
||||
mutable QDBusMessage mProgressIndicatorSignal;
|
||||
int mPreviousProgressPosition = 0;
|
||||
|
||||
// Current track metadata
|
||||
qlonglong m_duration;
|
||||
PlaybackState m_playbackState = PlaybackStopped;
|
||||
QString m_currentTrackTitle;
|
||||
QUrl m_currentTrackUrl;
|
||||
QString m_currentTrackAlbum;
|
||||
QString m_currentTrackArtist;
|
||||
QUrl m_coverArtUrl;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
/***************************************************************************
|
||||
SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
***************************************************************************/
|
||||
|
||||
#include "mpris2.h"
|
||||
#include "mediaplayer2.h"
|
||||
#include "mediaplayer2player.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
|
||||
|
||||
#if defined Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
Mpris2::Mpris2(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Mpris2::initDBusService()
|
||||
{
|
||||
QString mspris2Name(QStringLiteral("org.mpris.MediaPlayer2.") + m_playerName);
|
||||
|
||||
bool success = QDBusConnection::sessionBus().registerService(mspris2Name);
|
||||
|
||||
// If the above failed, it's likely because we're not the first instance
|
||||
// or the name is already taken. In that event the MPRIS2 spec wants the
|
||||
// following:
|
||||
if (!success) {
|
||||
#if defined Q_OS_WIN
|
||||
success = QDBusConnection::sessionBus().registerService(mspris2Name + QLatin1String(".instance") + QString::number(GetCurrentProcessId()));
|
||||
#else
|
||||
success = QDBusConnection::sessionBus().registerService(mspris2Name + QLatin1String(".instance") + QString::number(getpid()));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (success) {
|
||||
m_mp2 = std::make_unique<MediaPlayer2>(this);
|
||||
m_mp2p = std::make_unique<MediaPlayer2Player>(this);
|
||||
|
||||
QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/mpris/MediaPlayer2"), this, QDBusConnection::ExportAdaptors);
|
||||
|
||||
connect(m_mp2.get(), &MediaPlayer2::raisePlayer, this, &Mpris2::raisePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
Mpris2::~Mpris2()
|
||||
= default;
|
||||
|
||||
QString Mpris2::playerName() const
|
||||
{
|
||||
return m_playerName;
|
||||
}
|
||||
|
||||
void Mpris2::setPlayerName(const QString &playerName)
|
||||
{
|
||||
if (m_playerName == playerName) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_playerName = playerName;
|
||||
|
||||
if (!m_playerName.isEmpty()) {
|
||||
if (!m_mp2) {
|
||||
initDBusService();
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT playerNameChanged();
|
||||
}
|
||||
|
||||
#include "moc_mpris2.cpp"
|
||||
@@ -0,0 +1,63 @@
|
||||
/***************************************************************************
|
||||
SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <[email protected]>
|
||||
SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <[email protected]>
|
||||
SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class MediaPlayer2;
|
||||
class MediaPlayer2Player;
|
||||
|
||||
class Mpris2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString playerName
|
||||
READ playerName
|
||||
WRITE setPlayerName
|
||||
NOTIFY playerNameChanged)
|
||||
|
||||
public:
|
||||
explicit Mpris2(QObject* parent = nullptr);
|
||||
~Mpris2() override;
|
||||
|
||||
[[nodiscard]] QString playerName() const;
|
||||
|
||||
MediaPlayer2Player *player() const {
|
||||
return m_mp2p.get();
|
||||
}
|
||||
|
||||
void setPlayerName(const QString &playerName);
|
||||
|
||||
Q_SIGNALS:
|
||||
void raisePlayer();
|
||||
|
||||
void playerNameChanged();
|
||||
|
||||
void playListModelChanged();
|
||||
|
||||
void audioPlayerManagerChanged();
|
||||
|
||||
void manageMediaPlayerControlChanged();
|
||||
|
||||
void headerBarManagerChanged();
|
||||
|
||||
void audioPlayerChanged();
|
||||
|
||||
void showProgressOnTaskBarChanged();
|
||||
|
||||
private:
|
||||
|
||||
void initDBusService();
|
||||
|
||||
std::unique_ptr<MediaPlayer2> m_mp2;
|
||||
std::unique_ptr<MediaPlayer2Player> m_mp2p;
|
||||
QString m_playerName;
|
||||
bool mShowProgressOnTaskBar = true;
|
||||
};
|
||||
Reference in new issue
Block a user