2026-04-11 15:37:11 +00:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
|
|
|
<plist version="1.0">
|
|
|
|
|
<dict>
|
|
|
|
|
<key>CFBundleDevelopmentRegion</key>
|
|
|
|
|
<string>en</string>
|
|
|
|
|
<key>CFBundleExecutable</key>
|
|
|
|
|
<string>MusicPlugin</string>
|
|
|
|
|
<key>CFBundleIdentifier</key>
|
|
|
|
|
<string>com.mioisland.plugin.music-player</string>
|
|
|
|
|
<key>CFBundleInfoDictionaryVersion</key>
|
|
|
|
|
<string>6.0</string>
|
|
|
|
|
<key>CFBundleName</key>
|
|
|
|
|
<string>Music Player</string>
|
|
|
|
|
<key>CFBundlePackageType</key>
|
|
|
|
|
<string>BNDL</string>
|
|
|
|
|
<key>CFBundleShortVersionString</key>
|
v2.0.4: latency razor — event-driven + running-app gate + parallel probing
Target: push state-change detection latency under 200ms in the common case,
and cold start under 2s.
Changes:
1. Event-driven primary path, poll becomes safety-net
- Poll interval 1.5s → 15s. Was firing 40 AppleScript probes per minute
on a Mac that's playing nothing.
- MediaRemote notifications + DistributedNotificationCenter broadcasts
(com.spotify.client.PlaybackStateChanged,
com.apple.Music.playerInfo, com.apple.iTunes.playerInfo)
already handle track changes in <100ms. The 1.5s poll was just
backup, and now 15s is enough backup.
2. NSWorkspace launch/terminate observers
- New observers on NSWorkspace.didLaunchApplicationNotification +
didTerminateApplicationNotification. When Spotify, Apple Music, or
Chrome launches / quits, refresh fires immediately instead of
waiting for the next poll. Beats the old path by up to 15s on
first-launch-of-day scenarios.
3. Running-app gate (NSWorkspace.runningApplications)
- Each source now exposes `static var isRunning` via
NSWorkspace.shared.runningApplications.contains(bundleId).
- Router checks before probing. AppleScript `with timeout of 2 seconds`
still trips when the target app isn't running, so avoiding those
probes saves up to 6s per refresh on a clean Mac.
4. MediaRemote 15.4+ entitlement memoization
- When MRMediaRemoteGetNowPlayingInfo returns an empty dict AND at
least one player app is running (likelyBlocked heuristic), mark
MediaRemote blocked for 60s and skip in the router. Saves ~50ms
per refresh on restricted macOS versions and lets the first-pass
AppleScript probe happen without a preceding MR round-trip.
- Retries every 60s in case the gate state changes (macOS minor
update / user-granted entitlement).
5. Parallel fallback probing
- Old router was serial: MediaRemote → Spotify → Music → Chrome.
Cold start worst-case 4-6s when all three AppleScript sources
trip their 2s timeouts.
- New router uses `async let` to fan out every live candidate
concurrently. First-in-priority-order non-nil result wins.
Cold start worst-case now ≈ slowest single AppleScript probe.
6. Sticky-source fast path survives
- When the last-successful source is still a live candidate
(its app still running, MR still not blocked), try it alone
first. On steady-state playback this is one round-trip per
refresh, same as before.
7. Transport control perceived latency
- scheduleRefresh(after: 0.3) → 0.1 for togglePlay/next/prev/seek.
UI already flips optimistically; the 100ms re-sync is enough
to catch the real app state without feeling laggy.
Reference: Atoll (github.com/Ebullioscopic/Atoll) uses a bundled
mediaremote-adapter framework + Perl stream client to bypass the
macOS 15.4 MediaRemote entitlement gate entirely. That's a bigger
lift and left for a future phase — this commit wrings out the latency
that's achievable without that adapter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 07:02:36 +00:00
|
|
|
<string>2.0.4</string>
|
2026-04-11 15:37:11 +00:00
|
|
|
<key>CFBundleVersion</key>
|
v2.0.4: latency razor — event-driven + running-app gate + parallel probing
Target: push state-change detection latency under 200ms in the common case,
and cold start under 2s.
Changes:
1. Event-driven primary path, poll becomes safety-net
- Poll interval 1.5s → 15s. Was firing 40 AppleScript probes per minute
on a Mac that's playing nothing.
- MediaRemote notifications + DistributedNotificationCenter broadcasts
(com.spotify.client.PlaybackStateChanged,
com.apple.Music.playerInfo, com.apple.iTunes.playerInfo)
already handle track changes in <100ms. The 1.5s poll was just
backup, and now 15s is enough backup.
2. NSWorkspace launch/terminate observers
- New observers on NSWorkspace.didLaunchApplicationNotification +
didTerminateApplicationNotification. When Spotify, Apple Music, or
Chrome launches / quits, refresh fires immediately instead of
waiting for the next poll. Beats the old path by up to 15s on
first-launch-of-day scenarios.
3. Running-app gate (NSWorkspace.runningApplications)
- Each source now exposes `static var isRunning` via
NSWorkspace.shared.runningApplications.contains(bundleId).
- Router checks before probing. AppleScript `with timeout of 2 seconds`
still trips when the target app isn't running, so avoiding those
probes saves up to 6s per refresh on a clean Mac.
4. MediaRemote 15.4+ entitlement memoization
- When MRMediaRemoteGetNowPlayingInfo returns an empty dict AND at
least one player app is running (likelyBlocked heuristic), mark
MediaRemote blocked for 60s and skip in the router. Saves ~50ms
per refresh on restricted macOS versions and lets the first-pass
AppleScript probe happen without a preceding MR round-trip.
- Retries every 60s in case the gate state changes (macOS minor
update / user-granted entitlement).
5. Parallel fallback probing
- Old router was serial: MediaRemote → Spotify → Music → Chrome.
Cold start worst-case 4-6s when all three AppleScript sources
trip their 2s timeouts.
- New router uses `async let` to fan out every live candidate
concurrently. First-in-priority-order non-nil result wins.
Cold start worst-case now ≈ slowest single AppleScript probe.
6. Sticky-source fast path survives
- When the last-successful source is still a live candidate
(its app still running, MR still not blocked), try it alone
first. On steady-state playback this is one round-trip per
refresh, same as before.
7. Transport control perceived latency
- scheduleRefresh(after: 0.3) → 0.1 for togglePlay/next/prev/seek.
UI already flips optimistically; the 100ms re-sync is enough
to catch the real app state without feeling laggy.
Reference: Atoll (github.com/Ebullioscopic/Atoll) uses a bundled
mediaremote-adapter framework + Perl stream client to bypass the
macOS 15.4 MediaRemote entitlement gate entirely. That's a bigger
lift and left for a future phase — this commit wrings out the latency
that's achievable without that adapter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 07:02:36 +00:00
|
|
|
<string>6</string>
|
2026-04-11 15:37:11 +00:00
|
|
|
<key>NSPrincipalClass</key>
|
|
|
|
|
<string>MusicPlugin.MusicPlugin</string>
|
2026-04-19 12:42:14 +00:00
|
|
|
<!--
|
|
|
|
|
Optional size hint for the expanded plugin panel.
|
|
|
|
|
Host reads these on plugin load and caps the expanded area to the
|
|
|
|
|
requested dimensions instead of the default ~620x780. Both keys
|
|
|
|
|
must be present. Range: width 280-1200, height 180-900. Values
|
|
|
|
|
outside that range are ignored and the host falls back to default.
|
|
|
|
|
-->
|
|
|
|
|
<key>MioPluginPreferredWidth</key>
|
|
|
|
|
<integer>440</integer>
|
|
|
|
|
<key>MioPluginPreferredHeight</key>
|
|
|
|
|
<integer>340</integer>
|
2026-04-11 15:37:11 +00:00
|
|
|
</dict>
|
|
|
|
|
</plist>
|