名称: apple-music
版本: 0.6.0
描述: 通过 AppleScript (macOS) 或 MusicKit API 集成 Apple Music
本指南介绍如何与 Apple Music 集成。涵盖 AppleScript (macOS)、MusicKit API (跨平台) 以及关键的“库优先”要求。
当用户要求以下操作时调用:
- 管理播放列表(创建、添加/删除曲目、列出)
- 控制播放(播放、暂停、跳过、音量)
- 搜索目录或资料库
- 将歌曲添加到资料库
- 访问收听历史记录或推荐
不能直接将目录歌曲添加到播放列表。
歌曲必须先存在于用户的资料库中:
- ❌ 目录 ID → 播放列表(失败)
- ✅ 目录 ID → 资料库 → 播放列表(成功)
原因: 播放列表使用资料库 ID (i.abc123),而非目录 ID (1234567890)。
此规则同时适用于 AppleScript 和 API 方法。
| 功能 | AppleScript (macOS) | MusicKit API |
|---|---|---|
| 所需设置 | 无 | 开发者账户 + 令牌 |
| 播放列表管理 | 完整 | 仅限 API 创建的播放列表 |
| 播放控制 | 完整 | 无 |
| 目录搜索 | 否 | 是 |
| 资料库访问 | 即时 | 需要令牌 |
| 跨平台 | 否 | 是 |
零配置。可立即与 Music 应用配合使用。
通过 Bash 运行:
osascript -e 'tell application "Music" to playpause'
osascript -e 'tell application "Music" to return name of current track'
多行脚本:
osascript <<'EOF'
tell application "Music"
set t to current track
return {name of t, artist of t}
end tell
EOF
| 类别 | 操作 |
|---|---|
| 播放控制 | play, pause, stop, resume, next track, previous track, fast forward, rewind |
| 播放器状态 | player position, player state, sound volume, mute, shuffle enabled/mode, song repeat |
| 当前曲目 | name, artist, album, duration, time, rating, loved, disliked, genre, year, track number |
| 资料库 | search, list tracks, get track properties, set ratings |
| 播放列表 | list, create, delete, rename, add tracks, remove tracks, get tracks |
| AirPlay | list devices, select device, current device |
tell application "Music"
set t to current track
-- 基本信息
name of t -- "Hey Jude"
artist of t -- "The Beatles"
album of t -- "1 (Remastered)"
album artist of t -- "The Beatles"
composer of t -- "Lennon-McCartney"
genre of t -- "Rock"
year of t -- 1968
-- 时间信息
duration of t -- 431.0 (秒)
time of t -- "7:11" (格式化)
start of t -- 起始时间(秒)
finish of t -- 结束时间(秒)
-- 曲目信息
track number of t -- 21
track count of t -- 27
disc number of t -- 1
disc count of t -- 1
-- 评分
rating of t -- 0-100 (每星20分)
loved of t -- true/false
disliked of t -- true/false
-- 播放记录
played count of t -- 42
played date of t -- 上次播放日期
skipped count of t -- 3
skipped date of t -- 上次跳过日期
-- ID
persistent ID of t -- "ABC123DEF456"
database ID of t -- 12345
end tell
tell application "Music"
set t to current track
set rating of t to 80 -- 4 星
set loved of t to true
set disliked of t to false
set name of t to "New Name" -- 重命名曲目
set genre of t to "Alternative"
set year of t to 1995
end tell
tell application "Music"
player state -- stopped, playing, paused, fast forwarding, rewinding
player position -- 当前位置(秒,可读/写)
sound volume -- 0-100 (可读/写)
mute -- true/false (可读/写)
shuffle enabled -- true/false (可读/写)
shuffle mode -- songs, albums, groupings
song repeat -- off, one, all (可读/写)
current track -- 曲目对象
current playlist -- 播放列表对象
current stream URL -- 流媒体 URL
end tell
tell application "Music"
-- 播放控制
play -- 播放当前选中项
pause
stop
resume
playpause -- 切换播放/暂停
next track
previous track
fast forward
rewind
-- 播放特定内容
play (first track of library playlist 1 whose name contains "Hey Jude")
play user playlist "Road Trip"
-- 设置
set player position to 60 -- 跳转到 1:00
set sound volume to 50 -- 0-100
set mute to true
set shuffle enabled to true
set song repeat to all -- off, one, all
end tell
tell application "Music"
-- 所有资料库曲目
every track of library playlist 1
-- 按名称搜索
tracks of library playlist 1 whose name contains "Beatles"
-- 按艺术家搜索
tracks of library playlist 1 whose artist contains "Beatles"
-- 按专辑搜索
tracks of library playlist 1 whose album contains "Abbey Road"
-- 组合搜索
tracks of library playlist 1 whose name contains "Hey" and artist contains "Beatles"
-- 按流派
tracks of library playlist 1 whose genre is "Rock"
-- 按年份
tracks of library playlist 1 whose year is 1969
-- 按评分
tracks of library playlist 1 whose rating > 60 -- 3 星以上
-- 喜爱的曲目
tracks of library playlist 1 whose loved is true
-- 最近播放(按播放日期排序)
tracks of library playlist 1 whose played date > (current date) - 7 * days
end tell
tell application "Music"
-- 列出所有播放列表
name of every user playlist
-- 获取播放列表
user playlist "Road Trip"
first user playlist whose name contains "Road"
-- 创建播放列表
make new user playlist with properties {name:"New Playlist", description:"My playlist"}
-- 删除播放列表
delete user playlist "Old Playlist"
-- 重命名播放列表
set name of user playlist "Old Name" to "New Name"
-- 获取播放列表曲目
every track of user playlist "Road Trip"
name of every track of user playlist "Road Trip"
-- 添加曲目到播放列表(必须是资料库曲目)
set targetPlaylist to user playlist "Road Trip"
set targetTrack to first track of library playlist 1 whose name contains "Hey Jude"
duplicate targetTrack to targetPlaylist
-- 从播放列表移除曲目
delete (first track of user playlist "Road Trip" whose name contains "Hey Jude")
-- 播放列表属性
duration of user playlist "Road Trip" -- 总时长
time of user playlist "Road Trip" -- 格式化时长
count of tracks of user playlist "Road Trip"
end tell
tell application "Music"
-- 列出 AirPlay 设备
name of every AirPlay device
-- 获取当前设备
current AirPlay devices
-- 设置输出设备
set current AirPlay devices to {AirPlay device "Living Room"}
-- 多设备
set current AirPlay devices to {AirPlay device "Living Room", AirPlay device "Kitchen"}
-- 设备属性
set d to AirPlay device "Living Room"
name of d
kind of d -- computer, AirPort Express, Apple TV, AirPlay device, Bluetooth device
active of d -- true 如果正在播放
available of d -- true 如果可访问
selected of d -- true 如果在当前设备中
sound volume of d -- 0-100
end tell
始终转义用户输入:
def escape_applescript(s):
return s.replace('\\', '\\\\').replace('"', '\\"')
safe_name = escape_applescript(user_input)
script = f'tell application "Music" to play user playlist "{safe_name}"'
跨平台,但需要 Apple 开发者账户($99/年)和令牌设置。
要求:
1. Apple 开发者账户
2. 来自开发者门户的 MusicKit 密钥 (.p8 文件)
3. 开发者令牌 (JWT,最长 180 天)
4. 用户音乐令牌 (浏览器 OAuth)
生成开发者令牌:
import jwt, datetime
with open('AuthKey_XXXXXXXXXX.p8') as f:
private_key = f.read()
token = jwt.encode(
{
'iss': 'TEAM_ID',
'iat': int(datetime.datetime.now().timestamp()),
'exp': int((datetime.datetime.now() + datetime.timedelta(days=180)).timestamp())
},
private_key,
algorithm='ES256',
headers={'alg': 'ES256', 'kid': 'KEY_ID'}
)
获取用户令牌: 浏览器 OAuth 访问 https://authorize.music.apple.com/woa
所有请求的请求头:
Authorization: Bearer {developer_token}
Music-User-Token: {user_music_token}
基础 URL: https://api.music.apple.com/v1
| 端点 | 方法 | 描述 |
|---|---|---|
/catalog/{storefront}/search |
GET | 搜索歌曲、专辑、艺术家、播放列表 |
/catalog/{storefront}/songs/{id} |
GET | 歌曲详情 |
/catalog/{storefront}/albums/{id} |
GET | 专辑详情 |
/catalog/{storefront}/albums/{id}/tracks |
GET | 专辑曲目 |
/catalog/{storefront}/artists/{id} |
GET | 艺术家详情 |
/catalog/{storefront}/artists/{id}/albums |
GET | 艺术家的专辑 |
/catalog/{storefront}/artists/{id}/songs |
GET | 艺术家的热门歌曲 |
/catalog/{storefront}/artists/{id}/related-artists |
GET | 相似艺术家 |
/catalog/{storefront}/playlists/{id} |
GET | 播放列表详情 |
/catalog/{storefront}/charts |
GET | 热门排行榜 |
/catalog/{storefront}/genres |
GET | 所有流派 |
/catalog/{storefront}/search/suggestions |
GET | 搜索自动补全 |
/catalog/{storefront}/stations/{id} |
GET | 广播电台 |
| 端点 | 方法 | 描述 |
|---|---|---|
/me/library/songs |
GET | 所有资料库歌曲 |
/me/library/albums |
GET | 所有资料库专辑 |
/me/library/artists |
GET | 所有资料库艺术家 |
/me/library/playlists |
GET | 所有资料库播放列表 |
/me/library/playlists/{id} |
GET | 播放列表详情 |
/me/library/playlists/{id}/tracks |
GET | 播放列表曲目 |
/me/library/search |
GET | 搜索资料库 |
/me/library |
POST | 添加到资料库 |
/catalog/{sf}/songs/{id}/library |
GET | 从目录 ID 获取资料库 ID |
| 端点 | 方法 | 描述 |
|---|---|---|
/me/library/playlists |
POST | 创建播放列表 |
/me/library/playlists/{id}/tracks |
POST | 添加曲目到播放列表 |
| 端点 | 方法 | 描述 |
|---|---|---|
/me/recommendations |
GET | 个性化推荐 |
/me/history/heavy-rotation |
GET | 频繁播放 |
/me/recent/played |
GET | 最近播放 |
/me/recent/added |
GET | 最近添加 |
| 端点 | 方法 | 描述 |
|---|---|---|
/me/ratings/songs/{id} |
GET | 获取歌曲评分 |
/me/ratings/songs/{id} |
PUT | 设置歌曲评分 |
/me/ratings/songs/{id} |
DELETE | 移除评分 |
/me/ratings/albums/{id} |
GET/PUT/DELETE | 专辑评分 |
/me/ratings/playlists/{id} |
GET/PUT/DELETE | 播放列表评分 |
| 端点 | 方法 | 描述 |
|---|---|---|
/storefronts |
GET | 所有商店区域 |
/storefronts/{id} |
GET | 商店区域详情 |
/me/storefront |
GET | 用户的商店区域 |
| 参数 | 描述 | 示例 |
|---|---|---|
term |
搜索词 | term=beatles |
types |
资源类型 | types=songs,albums |
limit |
每页结果数(最多 25) | limit=10 |
offset |
分页偏移量 | offset=25 |
include |
相关资源 | include=artists,albums |
extend |
额外属性 | extend=editorialNotes |
l |
语言代码 | l=en-US |
GET /v1/catalog/us/search?term=wonderwall&types=songs&limit=10
响应:
{
"results": {
"songs": {
"data": [{
"id": "1234567890",
"type": "songs",
"attributes": {
"name": "Wonderwall",
"artistName": "Oasis",
"albumName": "(What's the Story) Morning Glory?",
"durationInMillis": 258773,
"releaseDate": "1995-10-02",
"genreNames": ["Alternative", "Music"]
}
}]
}
}
}
将目录歌曲添加到播放列表需要 4 个 API 调用:
import requests
headers = {
"Authorization": f"Bearer {dev_token}",
"Music-User-Token": user_token
}
# 1. 搜索目录
r = requests.get(
"https://api.music.apple.com/v1/catalog/us/search",
headers=headers,
params={"term": "Wonderwall Oasis", "types": "songs", "limit": 1}
)
catalog_id = r.json()['results']['songs']['data'][0]['id']
# 2. 添加到资料库
requests.post(
"https://api.music.apple.com/v1/me/library",
headers=headers,
params={"ids[songs]": catalog_id}
)
# 3. 获取资料库 ID (目录 ID → 资料库 ID)
r = requests.get(
f"https://api.music.apple.com/v1/catalog/us/songs/{catalog_id}/library",
headers=headers
)
library_id = r.json()['data'][0]['id']
# 4. 添加到播放列表(仅限资料库 ID!)
requests.post(
f"https://api.music.apple.com/v1/me/library/playlists/{playlist_id}/tracks",
headers={**headers, "Content-Type": "application/json"},
json={"data": [{"id": library_id, "type": "library-songs"}]}
)
POST /v1/me/library/playlists
Content-Type: application/json
{
"attributes": {
"name": "Road Trip",
"description": "Summer vibes"
},
"relationships": {
"tracks": {
"data": []
}
}
}
# 喜爱一首歌 (值: 1 = 喜爱, -1 = 不喜欢)
PUT /v1/me/ratings/songs/{id}
Content-Type: application/json
{"attributes": {"value": 1}}