_
________________( )_____
.-' o `-.
.' . \
/ | ,-"
| ____.'--'
\ __.-'
`.| |`----------'| |'
|__| |__|
一支住在終端機裡的音樂 CLI。搜尋、遙控播放,把 Spotify 與 Apple Music 的播放清單同步到你自己的 Google Drive。
三個步驟,由下而上:先讓 capy 能碰到你的音樂平台,再決定要不要把清單同步到雲端。
capy 不代持任何人的憑證,所以 Spotify 那一半要用你自己的 app。到 Spotify Developer Dashboard 建一個,把 redirect URI 設成 http://127.0.0.1:8888/callback。這裡必須是 127.0.0.1,寫 localhost Spotify 會拒絕。
$ capy auth login spotify
# 精靈會問 client ID,然後開瀏覽器完成授權
不需要其他設定就能開始用。搜尋會直接印出結果,在終端機裡是對齊的表格,在管線裡是乾淨的 TSV。
$ capy search 派對動物
$ capy play 五月天 # 藝人:播熱門歌曲
$ capy now # 現在在播什麼
播放清單的正本存在你自己 Google Drive 的應用程式資料夾裡(只有 capy 看得到,你隨時可以清空)。只有這一步需要登入 Google,而且權限只有三個:身分、電子郵件、應用程式資料夾。
$ capy auth login google
$ capy pl link 通勤 spotify:通勤 # 把平台清單連上來
$ capy pl sync 通勤 # 雙向同步
在終端機裡直接輸入 capy,不帶任何參數。開場的水豚會轉轉耳朵、眨眨眼、把嘴邊的牧草吃短再叼一根。終端機夠大(至少 30 行高)的話牠就留在畫面底部繼續過日子——偶爾眨眼、撥耳朵,隔一陣子吃一根草;打 / 開選單時牠先讓位,執行命令時牠也先讓開。終端機比較小的話,牠大約三秒後定格(按任何鍵可以跳過),之後畫面只有底部四行會變動。不想要任何動畫的話設 CAPY_MOTION=never,牠直接定格、之後一格都不動。不管哪一種,上面那一整片都是終端機自己的捲動區——你打過的命令、它們的輸出、發生過的錯誤都留在那裡,可以往回捲、可以複製。放進管線或 cron 時它仍然只印說明,不會變成一個等你按鍵的程式。
_
________________( )_____
.-' o `-.
.' . \
/ | ,-"
| ____.'--'
\ __.-'
`.| |`----------'| |'
|__| |__|
capy · spotify
> pl list
ID 名稱 曲數
p.b16G4rbSY0DzME 日本都會之聲 -
p.JL68l7EhogEeXd 冬日暖調 -
------------------------------------------------------
▶ 派對動物 · 1:23 / 4:09 · MacBook Pro · 音量 50
> _
space 播放/暫停 · ←→ ±10 秒 · / 命令 · ? 按鍵 · q 離開
pl list 列出我的播放清單
> pl show 顯示清單內容(不帶參數且在終端機裡會開挑選器)
pl sync 先 pull 再 push 的一輪:一張表、一次確認
------------------------------------------------------
▶ 派對動物 · 1:23 / 4:09 · MacBook Pro · 音量 50
> /pl s_
↑↓ 選 · Tab/⏎ 補齊 · Esc 收起
按 / 開命令選單:打字即時過濾,Tab 或 ⏎ 把命令帶進輸入行讓你補參數。打到完整命令(例如 /pl show 冬日暖調)選單就收起,⏎ 直接執行,開頭的 / 會自動拿掉。選單向上長、壓在捲動區前面,不會插進你的歷程。清單是直接從命令樹長出來的,不是另外維護的一份。
在那一行輸入的命令會重新執行 capy 自己,不是在同一個行程裡跑。所以每個子命令都保有它原本的樣子:該有表格的有表格,該問你確認的會問。清單名有空白時用雙引號,例如 pl show "上班 通勤"。
讀不到播放狀態時,完整的錯誤會印進捲動區留著,底下那行只留一句短的;連續五次讀不到就停止輪詢,按 r 重新連上。命令列在任何情況下都能用——連不上的時候,你最需要的正是它(doctor、auth login)。
capy --web 在你這台電腦上起一個只綁 127.0.0.1 的網頁介面,啟動時印一行帶一次性 token 的網址,在終端機裡會順便替你開瀏覽器(放進管線或背景執行時只印網址)。
打開就是「搬家」:把一個平台的播放清單搬到另一個平台,三步做完。選來源與目的地(每個平台的連接狀態一眼看得到,沒連的可以當場連)→ 挑一個清單、決定建新的還是加進既有的 → 看過要搬哪些歌、確認了才寫入。比對每一首歌的時候有真的進度,搬不過去的歌會逐首列出來。不刪來源、只新增、順序不動;不用付費、沒有曲數上限,因為它在你自己的電腦上用你自己的帳號跑。
先說限制:Apple Music 目前只能當來源;能在目的地建新清單的目前只有 Spotify,搬進本機曲庫只能加進既有的 M3U 檔;第一次連接帳號要花幾分鐘(Spotify 要用你自己建的 app、Apple 要自己複製權杖),換來的是沒有人替你代管憑證。硬碟裡的 M3U 播放清單也可以搬到 Spotify。
其他頁面:我的清單、同步、搜尋、帳號;「進階」裡是主控台(跟終端機一樣可以打任何子命令)、ISRC 查詢(一次問三個平台)與診斷。每一頁的底部一直有正在播放列(播放狀態面板):現在放什麼、進度、播放控制,capy now 的內容都在那裡。頁面上的每一個動作背後都是一條 capy 命令,主控台留著完整紀錄;要確認的事一律由命令自己問,頁面不會替你按。
網頁只在你這台電腦上:只綁 127.0.0.1、不是區網服務,每次啟動產生一次性 token,行程結束網址就失效。憑證不會經過頁面 —— 精靈輸入的 secret 只從瀏覽器送進行程再進 keychain,命令回聲裡的 token 值一律遮成 ***。
一次跑一個命令(第二個會被擋),因為它跟終端機共用同一份 Drive 與本機資料。跑著的時候底部多一列執行狀態:現在在做什麼、跑了多久、它最新印的那一行(比對歌曲時是做到第幾首),旁邊的「中止」隨時可以停(在主控台的命令列按 Ctrl-C 也行);已經答應寫入的命令要按兩次,停在一半可能只寫了一部分。有幾個命令在網頁上不提供:debug 群組、--auto、三個 secret flag(請走精靈)、now --watch(看面板就好);capy update 可以跑,但更新完這個網頁行程還是舊版,會要你重啟。
終端機的互動式介面是重新執行 capy 自己,所以每個子命令都保有它原本的樣子;網頁介面則在同一個行程裡跑並由頁面回答提示。兩條路不同是因為終端機已經有 TTY 可以讓給子行程,瀏覽器沒有。
下面按主題分組。每個命令在終端機裡是給人看的表格(放不下時儲存格換行、不截斷,ID 永遠完整;比終端機還寬時先開一個檢視窗格:每列一行,← → 橫向、↑ ↓ 上下、g G 頭尾、q 離開,Ctrl-C 則是中止整個命令;離開後表格以換行的形式留在捲動區。帶 --yes 的命令不開窗格,CAPY_PAGER=never 一律不開),在管線裡是 TSV,可以直接餵給 cut 與 awk。
--provider apple 換平台,--limit 控制筆數。artist: pl: track: 三種,等同 --type。1:05:30,腳本裡用純秒數 83 也可以。--watch 會持續更新並顯示進度條。play --device 指定。capy pl dedup apple:冬日暖調 則直接讀平台清單、只印報告(不碰 Drive、不需要連結),Apple 只讀,照表在 app 裡手動刪。這一組命令不帶清單名字直接打,在終端機裡會開挑選器讓你選:pl link 問三次(平台 → 那個平台上的哪個清單,在 Spotify 也可以選「建一個新的空清單」 → 連到哪個正本,這一步也可以當場建一個新的),pl unlink 問兩次。接在管線後面或放進 cron 時不會問,照舊要求你把參數寫齊。
none 表示這首在那個平台沒有。能力差異來自平台本身,不是 capy 偷懶。差在哪裡,命令會直接告訴你,而不是靜靜跳過。
| 能力 | Spotify | Apple Music | 本機曲庫 |
|---|---|---|---|
| 搜尋 | 可以 | 可以 | 曲庫內比對 |
| 播放遙控 | Connect | 只有 macOS | 不支援 |
| 讀取清單 | 可以 | 可以 | M3U 檔案 |
| 寫回清單 | 可以 | 尚未開放 | 可以 |
| 建立清單 | 可以 | 尚未開放 | 自己建檔 |
| 憑證來源 | 你自建的 app | 你從網頁播放器複製的權杖 | 不需要 |
一個命令就好。以 Apple Music 的「公路旅行」複製到 Spotify 為例;Spotify、Apple Music、Google 三個都要先登入。
$ capy migrate 公路旅行 --from apple --to spotify # 在 Spotify 建一個同名的私人清單,把曲目搬過去
$ capy migrate 公路旅行 --from apple --to spotify:開車歌單 # 或加進既有的清單:接在它原本的曲目後面
$ capy migrate # 不帶參數:逐段挑選來源、清單、目標
它先列出要搬什麼,你確認後才動手;需要新清單時也是確認之後才建。目標原本的順序是前綴,來源的曲目依來源的順序接在後面;來源裡目標已經有的(同一首歌,或同一個 ISRC)略過。永遠不動來源,對目標也只新增:目標有還沒同步的移除或換序時會先擋下來,請你先 capy pl sync。沒在目標平台找到的歌這次不推,終端機裡可以當場一首一首裁決,否則結尾會給你補上的命令。完成後只有目標連著正本(來源不連結,一次性複製);想之後跟著來源的變動,結尾也會給 pl link 加 pl sync 的命令。例外:正本本來就連著來源(手動流程做到一半)時,來源那半也一起拉進正本、以正本為準,結尾會說兩邊都連著。目標不能是 Apple(目前只讀)。
背後在做什麼:讓兩個平台連到同一個正本,再推過去。想讓兩邊持續同步的話,照下面手動做一次,之後就 capy pl sync。
先連 Apple 的清單,正本不存在會自動建立。再用 --create 在 Spotify 建一個同名的私人空清單,連到同一個正本。
$ capy pl link 公路旅行 apple:公路旅行
$ capy pl link 公路旅行 spotify --create
不要帶 --provider:Apple 那邊把曲目拉進正本;Spotify 那邊是空的,只記下這台裝置看過它的樣子。少了這一步,後面的 push 會被擋下來。
$ capy pl pull 公路旅行
有 ISRC 的曲目直接精準比對;沒有的靠標題、藝人、時長模糊比對,分數不夠的排進佇列等你裁決。
$ capy resolve 公路旅行 --provider spotify
$ capy resolve --review # 上一步有列出佇列才需要
表裡的 skip 列是這次複製不過去的曲目:還沒對應到 Spotify 的歌,通常是你自己上傳到 Apple 的,或 Spotify 上根本沒有的。
$ capy pl push 公路旅行 --provider spotify --dry-run
$ capy pl push 公路旅行 --provider spotify
整個過程不會刪到任何東西:Spotify 那邊是第一次拉,推過去的全是新增。之後兩邊保持連結,Apple 清單有變動時跑 capy pl sync 公路旅行 就會帶過去;只想複製一次的話,用上面的 capy migrate,或完成後 capy pl unlink 公路旅行 apple 解開。反方向(Spotify 到 Apple)目前還做不到:Apple 那一側只讀不寫。
建清單與推曲目是照 Spotify 2026 年 2 月的官方文件實作的,還沒有在真的帳號上跑過。遇到 404,或建出來的清單在 app 裡是公開的,請回報。
硬碟裡的 M3U 檔案也可以當成一個平台,跟 Spotify 雙向同步。它綁在這台機器上:換一台電腦,那邊的 capy 會跳過這個連結,不會把它當成清單被刪掉了。
$ capy config set local_root ~/Music/playlists
$ capy pl link 通勤 local:通勤.m3u8
$ capy pl sync 通勤
曲目的中繼資料放在同一個目錄的 library.json 裡,由你自己維護。有填 ISRC 的曲目才能跟串流平台上的同一首歌自動對上,沒填的就是各自獨立的一首。寫回時是整檔重寫,別的工具寫在 M3U 裡的註解與 #EXTINF 會被蓋掉。
不是在我們的伺服器上,因為沒有伺服器。本機的 SQLite 只是快取:刪掉它,下一次執行會從 Drive 完整重建。
任何會刪掉清單曲目的操作都會先列出來讓你確認,而且超過門檻時會直接擋下來,要加 --force 才放行。--dry-run 可以只看不做。
capy 沒有任何路徑會排序或打亂清單:同步只在平台自己重排時才跟著動,去重只拿掉後出現的那份,沒動到的曲目相對順序永遠不變。
那不是 Apple 官方支援的用法,會過期,風險由你自己承擔。capy 只會指導你怎麼複製,不會去讀你的瀏覽器 cookie,也不會注入任何指令碼。
寫回 Apple 的清單還沒開放,要先驗證過那組 API 真的能移除與重排曲目。在那之前,pl push --provider apple 會明講不支援,--all 則會跳過它並說明原因。
不在終端機的時候一律輸出純文字 TSV,沒有顏色也沒有互動提示。結束碼有意義:0 成功,1 錯誤,2 需要你介入,3 是安全閥擋下來了。