chore: add fallback media info

如果没有提供默认专辑和歌手信息图标等返回一个默认的图标和文字
如 firefox 播放酷狗音乐只有一个title,其他歌曲信息都没的

Issue: https://github.com/linuxdeepin/developer-center/issues/5786,https://github.com/linuxdeepin/developer-center/issues/4331
This commit is contained in:
ck 2023-12-21 13:50:17 +08:00 committed by mike
parent d765d60b22
commit e02ff1ac53
3 changed files with 147 additions and 80 deletions

View File

@ -13,6 +13,8 @@
#include <QJsonObject>
#include <QMetaMethod>
#include <QDBusAbstractInterface>
#include <QMetaObject>
#include <QUrl>
MediaPlayerModel::MediaPlayerModel(QObject *parent)
: QObject(parent)
@ -56,42 +58,22 @@ MediaPlayerModel::PlayStatus MediaPlayerModel::status()
const QString MediaPlayerModel::name()
{
if (m_mediaInter) {
Dict data = m_mediaInter->metadata();
return data["xesam:title"].toString();
}
return QString();
return m_name;
}
const QString MediaPlayerModel::iconUrl()
{
if (m_mediaInter) {
Dict data = m_mediaInter->metadata();
return data["mpris:artUrl"].toString();
}
return QString();
return m_icon;
}
const QString MediaPlayerModel::album()
{
if (m_mediaInter) {
Dict data = m_mediaInter->metadata();
return data["xesam:album"].toString();
}
return QString();
return m_album;
}
const QString MediaPlayerModel::artist()
{
if (m_mediaInter) {
Dict data = m_mediaInter->metadata();
return data["xesam:artist"].toString();
}
return QString();
return m_artist;
}
void MediaPlayerModel::setStatus(const MediaPlayerModel::PlayStatus &stat)
@ -122,30 +104,109 @@ void MediaPlayerModel::playNext()
m_mediaInter->Next();
}
void MediaPlayerModel::onServiceChanged()
void MediaPlayerModel::updateMetadata()
{
if (m_mediaInter) {
// 不论是新打开一个播放器还是关闭播放器都清理一下
if (!m_mediaInter)
return;
Dict v = m_mediaInter->metadata();
m_name = v.value("xesam:title").toString();
m_icon = v.value("mpris:artUrl").toString();
m_album = v.value("xesam:album").toString();
m_artist = v.value("xesam:artist").toString();
auto getName = [&v](const QString &service){
if (service.contains("vlc", Qt::CaseInsensitive)) {
const QString &url = v.value("xesam:url").toString();
if (!url.isEmpty())
return QUrl(url).fileName();
}
return tr("Unknown");
};
auto getIcon = [](const QString &service){
QMap<QString, QString> serv2Icon = {
{"vlc", "vlc"},
{"chromium", "chrome"},
{"firefox", "firefox"},
{"movie", "video"},
{"music", "music"}
};
for(auto k : serv2Icon.keys())
if (service.contains(k, Qt::CaseInsensitive))
return serv2Icon.value(k);
return QString("music");
};
const QString &service = m_mediaInter->service();
if (m_name.isEmpty())
m_name = getName(service);
if (m_icon.isEmpty())
m_icon = getIcon(service);
if (m_album.isEmpty())
m_album = tr("Unknown");
if (m_artist.isEmpty())
m_artist = tr("Unknown");
Q_EMIT MediaPlayerModel::metadataChanged();
}
void MediaPlayerModel::onServiceDiscovered(const QString &service)
{
auto mediaInter = new MediaPlayerInterface(service, "/org/mpris/MediaPlayer2",
QDBusConnection::sessionBus(), this);
// 影院不太希望被控制。。canShowInUI:false
if (!mediaInter->canControl() || !mediaInter->canShowInUI()) {
delete mediaInter;
return;
}
if (!m_mprisServices.contains(service))
m_mprisServices << service;
m_isActived = !m_mprisServices.isEmpty();
if (m_mediaInter && m_mediaInter->service() == service) {
Q_EMIT startStop(m_isActived);
delete mediaInter;
return;
}
if (m_mediaInter)
delete m_mediaInter;
m_mediaInter = mediaInter;
updateMetadata();
connect(m_mediaInter, &MediaPlayerInterface::PlaybackStatusChanged, this, [ this ] {
Q_EMIT statusChanged(convertStatus(m_mediaInter->playbackStatus()));
});
connect(m_mediaInter, &MediaPlayerInterface::MetadataChanged, this, &MediaPlayerModel::updateMetadata);
Q_EMIT startStop(m_isActived);
}
void MediaPlayerModel::onServiceDisappears(const QString &service)
{
if (!m_mprisServices.contains(service))
return;
m_mprisServices.removeAll(service);
m_isActived = !m_mprisServices.isEmpty();
if (m_mediaInter && m_mediaInter->service() == service) {
delete m_mediaInter;
m_mediaInter = nullptr;
}
m_isActived = !m_mprisServices.isEmpty();
if (m_isActived) {
m_mediaInter = new MediaPlayerInterface(m_mprisServices.last(), "/org/mpris/MediaPlayer2",
QDBusConnection::sessionBus(), this);
connect(m_mediaInter, &MediaPlayerInterface::PlaybackStatusChanged, this, [ this ] {
Q_EMIT statusChanged(convertStatus(m_mediaInter->playbackStatus()));
});
connect(m_mediaInter, &MediaPlayerInterface::MetadataChanged, this, &MediaPlayerModel::metadataChanged);
Dict v = m_mediaInter->metadata();
m_name = v.value("xesam:title").toString();
m_icon = v.value("mpris:artUrl").toString();
m_album = v.value("xesam:album").toString();
m_artist = v.value("xesam:artist").toString();
}
// 退出当前播放器后,继续控制上一个播放器
if (m_isActived)
return onServiceDiscovered(m_mprisServices.last());
Q_EMIT startStop(m_isActived);
}
@ -162,54 +223,30 @@ void MediaPlayerModel::initMediaPlayer()
QDBusReply<QStringList> reply = call.reply();
const QStringList &serviceList = reply.value();
auto serviceCanPlay = [](const QString &service){
QDBusInterface serviceInterface(service, "/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player",
QDBusConnection::sessionBus());
// 如果开启了谷歌浏览器的后台服务(org.mpris.MediaPlayer2.chromium.instance17352)
// 也符合名称要求,但是它不是音乐服务,此时需要判断是否存在这个属性
QVariant v = serviceInterface.property("CanPlay");
return v.isValid() && v.value<bool>();
};
for (const QString &serv : serviceList) {
if (!serv.startsWith("org.mpris.MediaPlayer2"))
continue;
if (!serviceCanPlay(serv)) {
qWarning() << "ignore invalid service" << serv;
continue;
}
m_mprisServices << serv;
break;
onServiceDiscovered(serv);
// break;
}
onServiceChanged();
QDBusConnectionInterface *dbusInterface = QDBusConnection::sessionBus().interface();
connect(dbusInterface, &QDBusConnectionInterface::serviceOwnerChanged, this,
[ = ](const QString &name, const QString &, const QString &newOwner) {
if (name.startsWith("org.mpris.MediaPlayer2")) {
if (newOwner.isEmpty()) {
m_mprisServices.removeAll(name);
} else if (serviceCanPlay(name)){
m_mprisServices << name;
} else {
qWarning() << "ignore invalid service" << name;
}
if (!name.startsWith("org.mpris.MediaPlayer2"))
return;
onServiceChanged();
if (newOwner.isEmpty()) {
onServiceDisappears(name);
} else {
onServiceDiscovered(name);
}
});
connect(dbusInterface, &QDBusConnectionInterface::serviceUnregistered, this,
[ = ](const QString &service) {
if (service.startsWith("org.mpris.MediaPlayer2")) {
m_mprisServices.removeAll(service);
onServiceChanged();
onServiceDiscovered(service);
}
});
});
@ -255,8 +292,13 @@ void MediaPlayerInterface::onPropertyChanged(const QDBusMessage &msg)
const QMetaObject* self = metaObject();
for (int i = self->propertyOffset(); i < self->propertyCount(); ++i) {
QMetaProperty p = self->property(i);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QGenericArgument value(QMetaType::typeName(p.type()), const_cast<void*>(changedProps[prop].constData()));
#else
QGenericArgument value{p.metaType().name(), const_cast<void*>(changedProps[prop].constData())};
#endif
if (p.name() == prop) {
Q_EMIT p.notifySignal().invoke(this);
Q_EMIT p.notifySignal().invoke(this, value);
}
}
}

View File

@ -54,7 +54,9 @@ Q_SIGNALS:
private:
void initMediaPlayer();
PlayStatus convertStatus(const QString &stat);
void onServiceChanged();
void onServiceDiscovered(const QString &service);
void onServiceDisappears(const QString &service);
void updateMetadata();
private:
bool m_isActived;
@ -99,6 +101,18 @@ public:
inline Dict metadata() const
{ return qvariant_cast<Dict>(property("Metadata")); }
Q_PROPERTY(bool CanControl READ canControl NOTIFY CanControlChanged)
inline bool canControl() const
{ return qvariant_cast< bool >(property("CanControl")); }
// from dtkwidget dbusmpris.h
Q_PROPERTY(bool CanShowInUI READ canShowInUI NOTIFY CanShowInUIChanged)
inline bool canShowInUI() const
{
QVariant showInUI = property("CanShowInUI");
// 属性有效且为假表示不能控制 无效或为真表示可以控制
return showInUI.isValid() ? showInUI.toBool() : true;
}
Q_PROPERTY(bool CanGoNext READ canGoNext NOTIFY CanGoNextChanged)
inline bool canGoNext() const
{ return qvariant_cast< bool >(property("CanGoNext")); }
@ -117,6 +131,8 @@ public:
Q_SIGNALS:
void MetadataChanged();
void CanControlChanged(bool value);
void CanShowInUIChanged(bool value);
void CanGoNextChanged();
void CanGoPreviousChanged();
void CanPauseChanged();

View File

@ -115,9 +115,18 @@ void MediaWidget::onUpdateMediaInfo()
{
m_musicName->setText(m_model->name());
QString file = m_model->iconUrl();
if (file.startsWith("file:///"))
QPixmap pixmap;
if (file.startsWith("file:///")) {
file.replace("file:///", "/");
m_musicIcon->setPixmap(QPixmap(file).scaled(m_musicIcon->size()));
pixmap = QPixmap(file).scaled(m_musicIcon->size());
}
if (pixmap.isNull()) {
// 就算给的文件不存在(e.g. deepin-music..),也至少 fallback 到 music 图标
pixmap = QIcon::fromTheme(file, QIcon::fromTheme("music")).pixmap(m_musicIcon->size());
}
m_musicIcon->setPixmap(pixmap);
m_musicSinger->setText(m_model->artist());
}