跳到主要內容
← 設計日記 聲音來賓
EP.02 · 設計日記

前 KKBOX 人的浪漫與現實

那些年,我在 KKBOX

在聊技術選型之前,讓我先倒帶一下。

我在 KKBOX 待過好幾年。那是一間很特別的公司 — 台灣少數專注做音樂的科技公司,辦公室裡的人聊的是歌單、聊的是音質、聊的是「那個歌手的新專輯你聽了沒」。

下午茶吃過烤山豬。對,整隻的那種。

跨年的時候,一群人窩在辦公室裡,用公司的音響系統放五月天的演唱會倒數。有人帶了啤酒,有人帶了鹹酥雞,12 點一到大家歡呼完,轉身繼續 debug。

那段日子裡,音樂不是我手機裡的背景白噪音,而是工作的一部分、生活的一部分、呼吸的一部分。

所以當我決定做一個跟音樂有關的 side project,腦子裡第一個跳出來的,當然是 KKBOX。


前東家情結

說實話,選 KKBOX API 的原因有 70% 是感情因素。

技術上的理由當然也有 — KKBOX 有全亞洲最完整的華語歌曲資料庫,歌名、歌手、專輯、年代,metadata 整理得乾乾淨淨。對一個猜歌遊戲來說,這些資料是核心中的核心。

但真正的原因是,我想跟老東家有一點連結。

像是你搬了家之後,偶爾還是會繞回老街區的那間早餐店吃蛋餅。不是因為那間最好吃,而是因為那裡有你的記憶。

我甚至幫專案取名叫 kkbox-sound-guest。KKBOX 三個字就這樣被釘進了檔案結構裡,像是一封寫給過去的明信片。


API 文件上的一行字

興高采烈地打開 KKBOX Open API 的文件,我開始規劃整個遊戲的資料流:

  1. 用 KKBOX Search API 搜歌 ✓
  2. 用 KKBOX Track API 拿歌曲詳情 ✓
  3. 拿到歌曲的音訊 URL,在前端播放 ✓
  4. 玩家聽片段猜歌 ✓

完美的計畫。直到第三步。

我翻遍了 Track API 的 response 欄位。name 有。album 有。artist 有。duration 有。

音訊 URL?

沒有。

我重新讀了一遍。沒有。再讀一遍。還是沒有。

KKBOX Open API 提供歌曲的所有 metadata — 名字、歌手、專輯、時長、甚至歌詞 — 但就是不提供音訊串流的 URL。

想想也是。音樂版權是 KKBOX 最核心的資產,怎麼可能透過公開 API 讓你自由播放?那跟把自己的商業模式開源沒兩樣。

道理我都懂。但那一刻,我坐在螢幕前,感覺像是約了老朋友吃飯,結果對方說「啊不好意思我只能遠端 say hi 不能到場」。


你會怎麼辦?

擺在我面前的選項:

A. 只用 metadata,不播音樂。 用文字題的方式:「以下哪首是周杰倫的歌?」但這樣就不是「猜歌」了,這是「考試」。沒有音樂的猜歌遊戲,就像沒有球的籃球比賽。

B. 引導使用者去 KKBOX app 聽。 技術上可行,但體驗爛到家。你能想像猜歌到一半,要跳到另一個 app 聽 30 秒,再跳回來選答案嗎?玩一輪猜四首就要切 8 次 app,我女兒第一題就會把平板丟了。

C. 找別的音樂來源。

我盯著螢幕看了大概十分鐘,然後打開 Google,搜了一句話:

“free music preview API no auth”


iTunes Search API:意外的救星

搜尋結果第一頁就出現了 Apple 的 iTunes Search API。

我抱著「不會這麼好吧」的心態點進去看文件,然後越看越不敢相信:

  • 完全免費 — 不要錢
  • 不需要 API key — 不需要註冊、不需要 OAuth、不需要填表單等審核
  • 30 秒 m4a 試聽 — 每首歌都有一段 30 秒的高品質音訊預覽
  • 全球曲庫 — Apple Music 有的它都有,涵蓋華語、日語、韓語、英語
  • 搜尋 + 排行榜 — 可以搜歌名、搜歌手,也能抓各國排行榜

我花了五分鐘寫了一個測試:

const res = await fetch(
  'https://itunes.apple.com/search?term=周杰倫+晴天&media=music&country=TW'
);
const data = await res.json();
console.log(data.results[0].previewUrl);

跑起來。拿到一個 .m4a 的 URL。點開。

周杰倫的聲音從喇叭裡流出來。

我靠在椅背上,忍不住笑了。


放下 KKBOX,擁抱 iTunes

做這個決定花了我大概一個晚上。

不是因為技術上有什麼難處 — iTunes API 比 KKBOX API 更簡單,連 API key 都不用設定。npm run dev 打下去就能跑,零設定。

猶豫的是那三個字母:kkbox

它還刻在我的專案名稱裡、刻在 package.json 的 description 裡、刻在好幾個檔案的 import path 裡。全部換掉,就像承認某段關係真的結束了。

但我想起一件事。

我做這個 project 的初衷,不是向前東家致敬,是讓女兒聽到我的歌。如果 KKBOX 的 API 做不到這件事,那再多的感情也沒有用。

那天晚上的 git log 大概長這樣:

feat: iTunes 試聽音源整合(解決 KKBOX 無音訊問題)
feat: 全面切換到 iTunes — 移除 KKBOX API 依賴
chore: 清理 KKBOX 殘留 + 更新品牌文字
fix: dns-prefetch 從 KKBOX 改為 iTunes

四個 commit,像四個告別的步驟。先引入新的,再移除舊的,最後打掃乾淨。

但我始終沒改專案的資料夾名稱。kkbox-sound-guest 還是 kkbox-sound-guest

有些東西不需要刪掉才能前進。它可以留在那裡,安靜地提醒你從哪裡來。


零成本,零設定

切換之後,整個開發體驗完全不同。

以前用 KKBOX API 的時候,每次有新同事(好吧,就是我自己不同裝置)要跑 dev,都要先去 KKBOX Developer Portal 申請 API key、設定環境變數、搞 OAuth token refresh。光是 setup 就要十分鐘。

現在?

git clone ...
npm install
npm run dev

三行指令,開始猜歌。

沒有 .env 檔案。沒有 API key。沒有 OAuth。沒有「你的 token 過期了請重新申請」。

這件事聽起來微不足道,但對一個 side project 來說意義重大。Side project 最大的敵人不是技術挑戰,是摩擦力。任何一個讓你多花十分鐘設定環境的東西,都是在消磨你「週六下午想打開來改一下」的動力。

iTunes Search API 把這個摩擦力降到了零。


30 秒的藝術

iTunes 給的是每首歌的 30 秒試聽片段。不多不少,剛好 30 秒。

一開始我覺得 30 秒太短了。一首歌四、五分鐘,只給 30 秒,會不會不夠?

後來我發現:30 秒其實太長了。

因為我的遊戲從 1 秒開始。

第一回合只聽 1 秒。第二回合 2 秒。第三回合 4 秒。大部分的歌,在 4 秒內就會被猜出來。30 秒?那根本是把整首歌送給你了。

Apple 挑選的 30 秒通常是歌曲的副歌或最具辨識度的段落,這對猜歌來說簡直是完美的。他們幫我做了最難的工作 — 選出每首歌「最好聽的那一段」。

但有個問題:iTunes 的音訊 URL 不能直接丟給前端。CORS 會擋掉。所以我寫了一個小小的音訊代理:

/api/audio/[trackId] → 代理請求到 iTunes → 回傳 m4a 給前端

就這樣。一個 proxy endpoint 解決所有問題。


下集預告

API 搞定了。音訊有了。

但下一個問題比技術更有趣 —

90 年代張學友的〈吻別〉跟 2024 年 NewJeans 的〈Super Shy〉,要怎麼放在同一個遊戲裡,讓我女兒和我都覺得好玩?

EP.03 — 四個年代,四種青春,下週見。


這個系列記錄了「聲音來賓」從零到上線的設計歷程。如果你也有 side project 的故事,歡迎跟我分享。