@@ -178,6 +178,7 @@ QString desktopEntryText(const QString &desktopFilePath, const QString &key);
178178QString localizedDesktopEntryText (const QString &desktopFilePath, const QString &key);
179179QString desktopEntryExecutable (const QString &desktopFilePath);
180180bool isBrowserDesktopId (const QString &desktopId);
181+ QString bestWindowIdForPid (qint64 pid, bool onlyVisible = true );
181182
182183QHash<QString, AiCliSessionLogCacheEntry> &aiCliSessionLogCache ()
183184{
@@ -364,18 +365,26 @@ bool desktopEntryLooksLikeMusicPlayer(const QString &desktopFilePath)
364365 QStringLiteral (" capture" ),
365366 QStringLiteral (" mixer" ),
366367 QStringLiteral (" volume" ),
368+ QStringLiteral (" effect" ),
369+ QStringLiteral (" effects" ),
370+ QStringLiteral (" equalizer" ),
371+ QStringLiteral (" enhancer" ),
372+ QStringLiteral (" processor" ),
373+ QStringLiteral (" filter" ),
367374 QStringLiteral (" converter" ),
368375 QStringLiteral (" editor" ),
376+ QStringLiteral (" podcast" ),
369377 QStringLiteral (" 剪辑" ),
370378 QStringLiteral (" 录音" ),
371379 QStringLiteral (" 混音" ),
380+ QStringLiteral (" 音效" ),
381+ QStringLiteral (" 均衡器" ),
372382 })) {
373383 return false ;
374384 }
375385
376386 return containsKeyword (combinedText, {
377387 QStringLiteral (" music" ),
378- QStringLiteral (" audio" ),
379388 QStringLiteral (" spotify" ),
380389 QStringLiteral (" netease" ),
381390 QStringLiteral (" cloudmusic" ),
@@ -437,6 +446,42 @@ const QHash<QString, QList<MusicDesktopCandidate>> &musicDesktopCandidatesByExec
437446 return candidateIndex;
438447}
439448
449+ bool isRecognizedMusicDesktopId (const QString &desktopId)
450+ {
451+ QString normalizedDesktopId = desktopId.trimmed ();
452+ if (normalizedDesktopId.endsWith (QStringLiteral (" .desktop" ))) {
453+ normalizedDesktopId.chop (QStringLiteral (" .desktop" ).size ());
454+ }
455+ if (normalizedDesktopId.isEmpty ()) {
456+ return false ;
457+ }
458+
459+ const auto &candidateIndex = musicDesktopCandidatesByExecutableBaseName ();
460+ for (auto it = candidateIndex.cbegin (); it != candidateIndex.cend (); ++it) {
461+ for (const MusicDesktopCandidate &candidate : it.value ()) {
462+ QString candidateDesktopId = candidate.desktopId .trimmed ();
463+ if (candidateDesktopId.endsWith (QStringLiteral (" .desktop" ))) {
464+ candidateDesktopId.chop (QStringLiteral (" .desktop" ).size ());
465+ }
466+ if (candidateDesktopId == normalizedDesktopId) {
467+ return true ;
468+ }
469+ }
470+ }
471+
472+ return false ;
473+ }
474+
475+ bool isRecognizedMusicExecutable (const QString &executablePath)
476+ {
477+ const QString executableBaseName = QFileInfo (executablePath).fileName ().trimmed ().toLower ();
478+ if (executableBaseName.isEmpty ()) {
479+ return false ;
480+ }
481+
482+ return musicDesktopCandidatesByExecutableBaseName ().contains (executableBaseName);
483+ }
484+
440485QStringList sessionLogPathsForProcess (qint64 pid)
441486{
442487 if (pid <= 0 ) {
@@ -980,6 +1025,10 @@ MusicSnapshot runningMusicPlayerSnapshot(const QString &previousDesktopEntry)
9801025
9811026 const QList<MusicDesktopCandidate> candidates = candidateIndex.value (executableBaseName);
9821027 for (const MusicDesktopCandidate &candidate : candidates) {
1028+ if (bestWindowIdForPid (pid).isEmpty ()) {
1029+ continue ;
1030+ }
1031+
9831032 int score = !candidate.executablePath .isEmpty () && candidate.executablePath == executablePath ? 300 : 220 ;
9841033 QString candidateDesktopId = candidate.desktopId ;
9851034 if (candidateDesktopId.endsWith (QStringLiteral (" .desktop" ))) {
@@ -994,6 +1043,7 @@ MusicSnapshot runningMusicPlayerSnapshot(const QString &previousDesktopEntry)
9941043 bestSnapshot.desktopEntry = candidate.desktopId ;
9951044 bestSnapshot.executablePath = executablePath;
9961045 bestSnapshot.appName = candidate.appName ;
1046+ bestSnapshot.canRaise = true ;
9971047 bestSnapshot.score = score;
9981048 bestPid = pid;
9991049 }
@@ -1759,7 +1809,7 @@ bool activateWindowForServiceOrDesktop(const QString &serviceName,
17591809 return activateWindow (windowId);
17601810}
17611811
1762- QString bestWindowIdForPid (qint64 pid, bool onlyVisible = true )
1812+ QString bestWindowIdForPid (qint64 pid, bool onlyVisible)
17631813{
17641814 if (pid <= 0 ) {
17651815 return {};
@@ -2158,17 +2208,29 @@ MusicSnapshot currentMusicSnapshot(const QString &previousService)
21582208 }
21592209
21602210 const QVariantMap rootProperties = dbusProperties (serviceName, QLatin1String (MprisRootInterface));
2211+ const uint servicePid = serviceProcessId (serviceName);
2212+ if (servicePid == 0 ) {
2213+ continue ;
2214+ }
2215+
21612216 const QString identity = stringFromDBusValue (rootProperties.value (QStringLiteral (" Identity" )));
21622217 const QString playbackStatus = stringFromDBusValue (playerProperties.value (QStringLiteral (" PlaybackStatus" )));
21632218 const QVariantMap metadata = mapFromDBusValue (playerProperties.value (QStringLiteral (" Metadata" )));
21642219
21652220 MusicSnapshot snapshot;
21662221 snapshot.service = serviceName;
21672222 snapshot.desktopEntry = stringFromDBusValue (rootProperties.value (QStringLiteral (" DesktopEntry" )));
2168- snapshot.executablePath = executablePathForPid (serviceProcessId (serviceName) );
2223+ snapshot.executablePath = executablePathForPid (servicePid );
21692224 if (snapshot.desktopEntry .isEmpty ()) {
21702225 snapshot.desktopEntry = desktopEntryFromMprisService (serviceName);
21712226 }
2227+
2228+ const bool recognizedMusicService = isRecognizedMusicDesktopId (snapshot.desktopEntry )
2229+ || isRecognizedMusicExecutable (snapshot.executablePath );
2230+ if (!recognizedMusicService) {
2231+ continue ;
2232+ }
2233+
21722234 snapshot.appName = identity;
21732235 snapshot.available = true ;
21742236 snapshot.playing = playbackStatus == QStringLiteral (" Playing" );
@@ -2201,6 +2263,11 @@ MusicSnapshot currentMusicSnapshot(const QString &previousService)
22012263 snapshot.artSource = stableMusicArtSource (artUrl);
22022264 }
22032265
2266+ const bool hasWindow = !bestWindowIdForPid (servicePid).isEmpty ();
2267+ if (!hasWindow && !snapshot.playing ) {
2268+ continue ;
2269+ }
2270+
22042271 snapshot.score = 0 ;
22052272 if (playbackStatus == QStringLiteral (" Playing" )) {
22062273 snapshot.score += 300 ;
@@ -2845,6 +2912,33 @@ void FashionLeftPluginProvider::openMusicPlayer()
28452912 }
28462913}
28472914
2915+ QString FashionLeftPluginProvider::musicControlThemeIconSource (const QString &iconName, bool darkTheme) const
2916+ {
2917+ const QString normalizedIconName = iconName.trimmed ();
2918+ if (normalizedIconName.isEmpty ()) {
2919+ return {};
2920+ }
2921+
2922+ const QString preferredTheme = darkTheme ? QStringLiteral (" Win11-dark" ) : QStringLiteral (" Win11" );
2923+ const QString fallbackTheme = darkTheme ? QStringLiteral (" Win11" ) : QStringLiteral (" Win11-dark" );
2924+ const QString iconPath = firstExistingPath ({
2925+ QStringLiteral (" /usr/share/icons/%1/actions/16/%2.svg" ).arg (preferredTheme, normalizedIconName),
2926+ QStringLiteral (" /usr/share/icons/%1/actions/22/%2.svg" ).arg (preferredTheme, normalizedIconName),
2927+ QStringLiteral (" /usr/share/icons/%1/actions/24/%2.svg" ).arg (preferredTheme, normalizedIconName),
2928+ QStringLiteral (" /usr/share/icons/%1/actions/16/%2.svg" ).arg (fallbackTheme, normalizedIconName),
2929+ QStringLiteral (" /usr/share/icons/%1/actions/22/%2.svg" ).arg (fallbackTheme, normalizedIconName),
2930+ QStringLiteral (" /usr/share/icons/%1/actions/24/%2.svg" ).arg (fallbackTheme, normalizedIconName),
2931+ QStringLiteral (" /usr/share/icons/bloom/actions/24/%1.svg" ).arg (normalizedIconName),
2932+ QStringLiteral (" /usr/share/icons/bloom/actions/22/%1.svg" ).arg (normalizedIconName),
2933+ QStringLiteral (" /usr/share/icons/Adwaita/symbolic/actions/%1-symbolic.svg" ).arg (normalizedIconName),
2934+ });
2935+ if (iconPath.isEmpty ()) {
2936+ return {};
2937+ }
2938+
2939+ return QUrl::fromLocalFile (iconPath).toString ();
2940+ }
2941+
28482942void FashionLeftPluginProvider::playPreviousTrack ()
28492943{
28502944 if (m_musicService.isEmpty () || !m_musicCanGoPrevious) {
@@ -3073,7 +3167,6 @@ void FashionLeftPluginProvider::refreshMusicState()
30733167 nextMusicAppName,
30743168 snapshot.service );
30753169 const QUrl nextMusicPlayerIconSource = iconSourceForName (nextMusicPlayerIconName);
3076-
30773170 if (m_musicService == snapshot.service
30783171 && m_musicDesktopEntry == nextDesktopEntry
30793172 && m_musicExecutablePath == nextMusicExecutablePath
@@ -3411,26 +3504,17 @@ void FashionLeftPluginProvider::refreshAiState()
34113504 nextToolEntries << entry;
34123505 }
34133506
3414- if (nextToolEntries.isEmpty ()) {
3415- QVariantMap idleEntry;
3416- idleEntry.insert (QStringLiteral (" toolId" ), nextPrimaryToolId);
3417- idleEntry.insert (QStringLiteral (" displayName" ), nextPrimaryToolId.isEmpty () ? QStringLiteral (" AI" ) : aiToolDisplayName (nextPrimaryToolId));
3418- idleEntry.insert (QStringLiteral (" processLabel" ), nextPrimaryToolId.isEmpty () ? QStringLiteral (" ai cli" ) : aiToolProcessLabel (nextPrimaryToolId));
3419- idleEntry.insert (QStringLiteral (" runningCount" ), 0 );
3420- idleEntry.insert (QStringLiteral (" completedCount" ), 0 );
3421- idleEntry.insert (QStringLiteral (" totalCount" ), 0 );
3422- idleEntry.insert (QStringLiteral (" progressText" ), QStringLiteral (" 0/0" ));
3423- nextToolEntries << idleEntry;
3424- }
3425-
34263507 int nextRunningCount = 0 ;
34273508 for (auto it = currentRunningCounts.cbegin (); it != currentRunningCounts.cend (); ++it) {
34283509 nextRunningCount += it.value ();
34293510 }
34303511
34313512 QString nextHeadlineText;
34323513 QString nextSummaryText;
3433- if (nextToolEntries.size () == 1 ) {
3514+ if (nextToolEntries.isEmpty ()) {
3515+ nextHeadlineText = QStringLiteral (" 0 条任务" );
3516+ nextSummaryText = QStringLiteral (" ai cli" );
3517+ } else if (nextToolEntries.size () == 1 ) {
34343518 const QVariantMap entry = nextToolEntries.constFirst ().toMap ();
34353519 nextHeadlineText = entry.value (QStringLiteral (" progressText" )).toString () + QStringLiteral (" 条任务" );
34363520 nextSummaryText = entry.value (QStringLiteral (" processLabel" )).toString ();
0 commit comments