904b9b3d-c0eb-42f3-acef-958.../README.md
徐翔宇 64daaa3371 v2.0.0: full rewrite with multi-source NowPlaying
Replaces the v1.0.0 shell (MediaRemote-only, non-functional on macOS 15.4+)
with a layered design that handles four playback sources with sticky source
priority routing:

  NowPlayingState (orchestrator, @MainActor, 3s poll + notifications)
    ├─ MediaRemote (private framework, dlopen)
    ├─ Spotify AppleScript (desktop)
    ├─ Apple Music AppleScript (desktop)
    └─ Chrome JS injection (YouTube / SoundCloud / web music)

UI:
  - Large album art with color-extracted gradient background
  - Title / artist / album + source badge
  - Draggable seek bar with hover-grow affordance
  - Prev / Play·Pause (56pt lime button) / Next controls
  - Header slot: 20x20 icon + 3-bar pseudo-spectrum that pulses while playing
  - Bi-lingual (zh / en), follows host appLanguage

Graceful degradation:
  - Host < v2.1.7 → upgrade banner (NSAppleEventsUsageDescription required)
  - QQ Music / NetEase / Kugou detected → "desktop unsupported, try web version"
  - Empty state with hint to play something in supported apps

Build layout:
  Sources/              root (MioPlugin.swift contract + MusicPlugin principal)
  Sources/sources/      data sources
  Sources/ui/           SwiftUI views
  Sources/support/      ChineseAppDetector / HostVersionCheck / Localization

build.sh now recursively finds .swift under Sources/.

Breaking-ish: plugin id ("music-player") and bundle ID preserved. Users on
v1.0.0 can upgrade in place via the plugin store.

Requires: MioIsland host >= v2.1.7 for full functionality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 02:27:21 +08:00

176 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Music Player Plugin for MioIsland
> **v2.0.0 — full rewrite.** Real Now Playing info from Spotify, Apple Music,
> Google Chrome (YouTube / SoundCloud / 网页版音乐), with playback controls,
> draggable seek bar, album art color tint, and a pseudo-spectrum in the header
> icon. Replaces the v1.0.0 shell that only wired up MediaRemote.
## Features
- **Multi-source playback tracking** with sticky source priority:
- MediaRemote (private framework, any app that registers with `MPNowPlayingInfoCenter`)
- Spotify desktop (AppleScript)
- Apple Music desktop (AppleScript)
- Google Chrome tabs (JavaScript injection into `<video>` / `<audio>` elements)
- **Full playback controls** — previous, play/pause, next, draggable seek bar
- **Album art color tint** — extracts dominant color from artwork, uses it as a
soft gradient background in the expanded view
- **Pseudo-spectrum in the Notch** — three animated vertical bars in the header
icon that pulse while music is playing
- **Bi-lingual** — follows MioIsland's `appLanguage` preference (zh / en)
- **Graceful degradation:**
- Host too old (< v2.1.7) shows upgrade banner instead of silently failing
- Chinese music app running (QQ 音乐 / 网易云 / 酷狗) shows "desktop not
supported, try the web version" hint
## Supported music apps
| App | Supported? | How |
|-----|------------|-----|
| **Spotify** desktop | Full | AppleScript + MediaRemote |
| **Apple Music** desktop | Full | AppleScript + MediaRemote |
| **YouTube / YouTube Music** in Chrome | Full | JS injection |
| **SoundCloud** in Chrome | Full | JS injection |
| **Spotify Web Player** in Chrome | Full | JS injection |
| **网易云音乐 / QQ 音乐 / 酷狗 (网页版)** in Chrome | Full | JS injection |
| **网易云音乐 / QQ 音乐** 新版桌面 app | 🟡 Partial | MediaRemote if the app registers (macOS < 15.4 ok; 15.4+ may silently drop) |
| **酷狗桌面 app / 酷我 / 咪咕 / 其他国产** | Not supported | No MediaRemote, no AppleScript API |
| Any app using `MPNowPlayingInfoCenter` | | MediaRemote |
> **macOS 15.4+ note:** Apple restricted `MRMediaRemoteGetNowPlayingInfo` to
> apps with special entitlements. The plugin auto-falls back to AppleScript
> for Spotify / Apple Music, and to JS injection for Chrome-based sources.
## Installation
### Prerequisite: MioIsland host v2.1.7 or newer
This plugin uses AppleScript to talk to Spotify / Apple Music / Chrome.
macOS requires the host app's `Info.plist` to declare `NSAppleEventsUsageDescription`
for that. MioIsland added this key in **v2.1.7** older hosts will show an
upgrade banner inside the plugin and skip AppleScript sources.
Upgrade the host:
```bash
brew upgrade codeisland
```
Or via MioIsland's in-app update (Sparkle will prompt automatically).
### From MioIsland Plugin Store (recommended)
1. Open **MioIsland Settings → Plugins**
2. Click **打开插件市场 (Open Plugin Store)** opens https://miomio.chat
3. Find **Music Player v2.0.0** and click **Install**
4. Copy the generated `https://api.miomio.chat/api/i/...` URL
5. Paste into the **Install from URL** field and click **Install**
6. Restart MioIsland (menu bar quit relaunch)
### Manual installation
```bash
# Download the latest release from GitHub
curl -LO https://github.com/MioMioOS/mio-plugin-music/releases/latest/download/music-player.zip
unzip music-player.zip
mkdir -p ~/.config/codeisland/plugins/
cp -R music-player.bundle ~/.config/codeisland/plugins/
# Restart MioIsland
```
### First-run permissions
When the plugin fetches track info for the first time, macOS will prompt:
> "Mio Island" would like to control "Spotify".app.
Click **OK**. Do the same for **Music.app** and **Google Chrome** when they
come up. These permissions are granted to the host app once and remembered
forever subsequent launches don't re-prompt.
If you accidentally clicked **Don't Allow**, fix it in:
**System Settings → Privacy & Security → Automation** toggle Mio Island on
for each target app.
### Chrome-specific setup
To get Chrome playback (YouTube, SoundCloud, etc.) working:
1. Open Chrome
2. Menu bar: **View → Developer → Allow JavaScript from Apple Events**
3. Check the option (click if unchecked)
This is a one-time setting that lives in Chrome's preferences. Without it,
AppleScript JS injection will silently return nothing for Chrome tabs.
## Building from source
Requirements:
- macOS 15.0+
- Xcode 16+ Command Line Tools
- Swift 5.10+
```bash
git clone https://github.com/MioMioOS/mio-plugin-music.git
cd mio-plugin-music
./build.sh # → build/music-player.bundle + build/music-player.zip
./build.sh install # (not implemented — copy manually)
```
Install the build output:
```bash
cp -R build/music-player.bundle ~/.config/codeisland/plugins/
```
Restart MioIsland.
## Plugin architecture (v2.0.0)
```
Sources/
├── MioPlugin.swift — plugin SDK protocol (DO NOT MODIFY)
├── MusicPlugin.swift — principal class (activate / makeView / header slot)
├── NowPlayingState.swift — @MainActor ObservableObject, source router
├── sources/
│ ├── MediaRemoteSource.swift — dlopen private MediaRemote.framework
│ ├── SpotifyAppleScript.swift
│ ├── AppleMusicAppleScript.swift
│ └── ChromeWebSource.swift — JS injection into <video> / <audio>
├── ui/
│ ├── ExpandedView.swift — main 620×780 panel
│ ├── HeaderSlotView.swift — 20×20 header icon + pseudo-spectrum
│ ├── AlbumArtColorExtractor.swift
│ └── SeekBar.swift
└── support/
├── ChineseAppDetector.swift — QQ / NetEase / Kugou detection
├── HostVersionCheck.swift — host ≥ v2.1.7 gate
└── Localization.swift — zh / en strings
```
**Source priority routing:** sticky (last successful) MediaRemote Spotify
Apple Music Chrome. First source that returns non-empty playback state wins.
3-second poll timer drives periodic refresh; Spotify and Music distributed
notifications trigger immediate refresh for instant reaction.
## Privacy
The plugin reads only:
- System-level Now Playing metadata (MediaRemote)
- Current track info from Spotify / Music / Chrome via AppleScript
- Bundle IDs of running apps to detect which source is active
It does **not** read:
- Your listening history
- Anything outside the "currently playing" state
- Anything from apps that are not music-related
Nothing is sent to any server. All processing happens locally.
## License
MIT. See LICENSE.
## Author
[@xmqywx](https://github.com/xmqywx) part of the [MioMioOS](https://github.com/MioMioOS)
official plugin set.