3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-02-23 07:35:54 +00:00
2022-03-05 22:41:29 +05:00

235 lines
7.5 KiB
C++

/*
SPDX-FileCopyrightText: 2012 Alex Merry <alex.merry@kdemail.net>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "multiplexer.h"
#include <mprisplayer.h>
#include <KLocalizedString>
#include <QAction>
#include <QDebug> // for Q_ASSERT
#include <algorithm>
#include "debug.h"
// the '@' at the start is not valid for D-Bus names, so it will
// never interfere with an actual MPRIS2 player
const QLatin1String Multiplexer::sourceName = QLatin1String("@multiplex");
Multiplexer::Multiplexer(QObject *parent)
: DataContainer(parent)
{
setObjectName(sourceName);
}
void Multiplexer::evaluatePlayer(PlayerContainer *container)
{
bool makeActive = m_activeName.isEmpty();
QString name = container->objectName();
const QString containerPlaybackStatus = container->data().value(QStringLiteral("PlaybackStatus")).toString();
const QString multiplexerPlaybackStatus = data().value(QStringLiteral("PlaybackStatus")).toString();
m_playing.remove(name);
m_paused.remove(name);
m_stopped.remove(name);
// Ensure the actual player is always in the correct category
if (containerPlaybackStatus == QLatin1String("Playing")) {
m_playing.insert(name, container);
} else if (containerPlaybackStatus == QLatin1String("Paused")) {
m_paused.insert(name, container);
} else {
m_stopped.insert(name, container);
}
const auto proxyPid = container->data().value(QStringLiteral("Metadata")).toMap().value(QStringLiteral("kde:pid")).toUInt();
if (proxyPid) {
auto it = m_proxies.find(proxyPid);
if (it == m_proxies.end()) {
m_proxies.insert(proxyPid, container);
}
}
const auto containerPid = container->data().value(QStringLiteral("InstancePid")).toUInt();
PlayerContainer *proxy = m_proxies.value(containerPid);
if (proxy) {
// Operate on the proxy from now on
container = proxy;
name = container->objectName();
}
if (!makeActive) {
// If this player has higher status than the current multiplexer player, switch over to it
if (m_playing.value(name) && multiplexerPlaybackStatus != QLatin1String("Playing")) {
qCDebug(MPRIS2) << "Player" << name << "is now playing but current was not";
makeActive = true;
} else if (m_paused.value(name) && multiplexerPlaybackStatus != QLatin1String("Playing") && multiplexerPlaybackStatus != QLatin1String("Paused")) {
qCDebug(MPRIS2) << "Player" << name << "is now paused but current was stopped";
makeActive = true;
}
}
if (m_activeName == name) {
// If we are the current player and move to a lower status, switch to another one, if necessary
if (m_paused.value(name) && !m_playing.isEmpty()) {
qCDebug(MPRIS2) << "Current player" << m_activeName << "is now paused but there is another playing one, switching players";
setBestActive();
makeActive = false;
} else if (m_stopped.value(name) && (!m_playing.isEmpty() || !m_paused.isEmpty())) {
qCDebug(MPRIS2) << "Current player" << m_activeName << "is now stopped but there is another playing or paused one, switching players";
setBestActive();
makeActive = false;
} else {
makeActive = true;
}
}
if (makeActive) {
if (m_activeName != name) {
qCDebug(MPRIS2) << "Switching from" << m_activeName << "to" << name;
m_activeName = name;
}
replaceData(container->data());
checkForUpdate();
Q_EMIT activePlayerChanged(container);
}
}
void Multiplexer::addPlayer(PlayerContainer *container)
{
evaluatePlayer(container);
connect(container, &Plasma::DataContainer::dataUpdated, this, &Multiplexer::playerUpdated);
}
void Multiplexer::removePlayer(const QString &name)
{
PlayerContainer *container = m_playing.take(name);
if (!container)
container = m_paused.take(name);
if (!container)
container = m_stopped.take(name);
if (container)
container->disconnect(this);
// Remove proxy by value (container), not key (pid), which could have changed
const auto pid = m_proxies.key(container);
if (pid) {
m_proxies.remove(pid);
}
if (name == m_activeName) {
setBestActive();
}
// When there is no player opened
if (m_paused.empty() && m_stopped.empty() && m_playing.empty()) {
Q_EMIT playerListEmptied();
}
}
PlayerContainer *Multiplexer::activePlayer() const
{
if (m_activeName.isEmpty()) {
return nullptr;
}
PlayerContainer *container = m_playing.value(m_activeName);
if (!container)
container = m_paused.value(m_activeName);
if (!container)
container = m_stopped.value(m_activeName);
Q_ASSERT(container);
return container;
}
void Multiplexer::playerUpdated(const QString &name, const Plasma::DataEngine::Data &newData)
{
Q_UNUSED(name);
Q_UNUSED(newData);
evaluatePlayer(qobject_cast<PlayerContainer *>(sender()));
}
PlayerContainer *Multiplexer::firstPlayerFromHash(const QHash<QString, PlayerContainer *> &hash, PlayerContainer **proxyCandidate) const
{
if (proxyCandidate) {
*proxyCandidate = nullptr;
}
auto it = hash.begin();
if (it == hash.end()) {
return nullptr;
}
PlayerContainer *container = it.value();
const auto containerPid = container->data().value(QStringLiteral("InstancePid")).toUInt();
// Check if this player is being proxied by someone else and prefer the proxy
// but only if it is in the same hash (same state)
if (PlayerContainer *proxy = m_proxies.value(containerPid)) {
if (std::find(hash.begin(), hash.end(), proxy) == hash.end()) {
if (proxyCandidate) {
*proxyCandidate = proxy;
}
return nullptr;
// continue;
}
return proxy;
}
return container;
}
void Multiplexer::setBestActive()
{
qCDebug(MPRIS2) << "Activating best player";
PlayerContainer *proxyCandidate = nullptr;
PlayerContainer *container = firstPlayerFromHash(m_playing, &proxyCandidate);
if (!container) {
// If we found a proxy earlier, prefer it over a random other player in that category
if (proxyCandidate && std::find(m_paused.constBegin(), m_paused.constEnd(), proxyCandidate) != m_paused.constEnd()) {
container = proxyCandidate;
} else {
container = firstPlayerFromHash(m_paused, &proxyCandidate);
}
}
if (!container) {
if (proxyCandidate && std::find(m_stopped.constBegin(), m_stopped.constEnd(), proxyCandidate) != m_stopped.constEnd()) {
container = proxyCandidate;
} else {
container = firstPlayerFromHash(m_stopped, &proxyCandidate);
}
}
if (!container) {
qCDebug(MPRIS2) << "There is currently no player";
m_activeName.clear();
removeAllData();
} else {
m_activeName = container->objectName();
qCDebug(MPRIS2) << "Determined" << m_activeName << "to be the best player";
replaceData(container->data());
checkForUpdate();
}
Q_EMIT activePlayerChanged(container);
}
void Multiplexer::replaceData(const Plasma::DataEngine::Data &data)
{
removeAllData();
Plasma::DataEngine::Data::const_iterator it = data.constBegin();
while (it != data.constEnd()) {
setData(it.key(), it.value());
++it;
}
setData(QStringLiteral("Source Name"), m_activeName);
}