From e02ff1ac534ac779a4f2d6e31a3d41f933b7eb04 Mon Sep 17 00:00:00 2001 From: ck Date: Thu, 21 Dec 2023 13:50:17 +0800 Subject: [PATCH] chore: add fallback media info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 如果没有提供默认专辑和歌手信息图标等返回一个默认的图标和文字 如 firefox 播放酷狗音乐只有一个title,其他歌曲信息都没的 Issue: https://github.com/linuxdeepin/developer-center/issues/5786,https://github.com/linuxdeepin/developer-center/issues/4331 --- plugins/media/mediaplayermodel.cpp | 196 +++++++++++++++++------------ plugins/media/mediaplayermodel.h | 18 ++- plugins/media/mediawidget.cpp | 13 +- 3 files changed, 147 insertions(+), 80 deletions(-) diff --git a/plugins/media/mediaplayermodel.cpp b/plugins/media/mediaplayermodel.cpp index 860c80f0d..7d09e30c3 100644 --- a/plugins/media/mediaplayermodel.cpp +++ b/plugins/media/mediaplayermodel.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include 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 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 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(); - - }; 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(changedProps[prop].constData())); +#else + QGenericArgument value{p.metaType().name(), const_cast(changedProps[prop].constData())}; +#endif if (p.name() == prop) { - Q_EMIT p.notifySignal().invoke(this); + Q_EMIT p.notifySignal().invoke(this, value); } } } diff --git a/plugins/media/mediaplayermodel.h b/plugins/media/mediaplayermodel.h index f24613cb9..9ea358e72 100644 --- a/plugins/media/mediaplayermodel.h +++ b/plugins/media/mediaplayermodel.h @@ -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(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(); diff --git a/plugins/media/mediawidget.cpp b/plugins/media/mediawidget.cpp index 055de43c5..be52c4ea2 100644 --- a/plugins/media/mediawidget.cpp +++ b/plugins/media/mediawidget.cpp @@ -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()); }