FFAudio - 今晚我想來首巴哈的 BWV1066 配炸雞腿
Demo 站
礙於版權問題,不能公開正式版,雖然文章中就有提及正式站的網址,但是正式站需要帳號才能登入。
功能
下一首曲子
這個 WebApp 主要的功能就是用於點播音樂,但是因為播放的音樂是古典樂,所以當初在構想的時候,希望在聽的時候能夠順序播放每個樂章,但是又可以隨機播放不同樂曲,所以在選下一首的策略為:
- 當前樂曲是否有下一個樂章
- 有,則播放下一個樂章
- 沒有,若為順序播放則播放下一首曲子,或隨機選一首取子,並播放被選中曲的第一樂章
串流播放
其實在較新瀏覽器中,透過AudioElement
來播放音樂,已經可以作到類似串流的載入方式了,瀏覽器在播放時先載入一段然後待快要到預載的部份時,再往後載入下一段,這樣其實就已經達到需求了,因為就算是自己實做一個串流,也需要像 Chrome 一樣的預載功能,因此在這裡我就是直接透過AudioElement
直接向CDN
索取檔案。
背景播放
在手機版中,希望可以作到就算關閉螢幕之後也可以自動往下一首播放,至於這個的具體實做請參照下面的文章連結:
客戶端
整個應用程式並沒有後端,僅有一個 Vue App 向一個靜態 API 取得 JSON 文件,這個 JSON 文件中含有曲目、名稱作曲家等資料,在應用程式載入初期就會直接將整個 JSON 下載下來作為播放清單,因為所有樂章的音樂數量大概最多只會有 1200 首左右,整個 JSON 並不會太大,整個載下來是沒有任何問題的。
音樂格式使用 AAC 編碼,主要是考慮到瀏覽器的相容性,如果使用 WAV 的話就算伺服器稱的住,對手機沒有吃到飽或是 Wifi 環境很差的用戶來說會降低體驗,而如果用 MP3 的話,音質可能會損失太多,因此選用在相同 Bitrate 下音質優於 MP3 的 AAC 格式。
當然後期有可能會新增音質模式以及效能模式,音質模式就串流 WAV 效能模式則串流 AAC。
儲存
我將儲存交給 Google Cloud Storage 來負責值得一提的是:我使用的儲存方案是 Nearline 而不是 Standard,是因為我在這個 Bucket 前面又加了一層 Cloudflare CDN 並用 Page Rule 將其設定為在邊緣伺服器保留 6 個月,因此理論上如果這些快取沒有因為 Cloudflare 的快取策略或者被我手動 Purge 掉,基本上每一個檔案 6 個月才會從 Google Cloud Storage 被訪問一次,而這個時間差不多就是符合 Nearline 的訪問頻率。
佈署
自動化的佈署使用 GitLab CI/CD 來進行應用程式的打包以及佈署,在 develop 分支上會自動化佈署至: https://dev_audio_floatflower_me.floatflower1029.workers.dev/, 而在 master 上則會佈署至正式站:https://audio.floatflower.me。
伺服器
因為是靜態的 Web Application 並不需要後端,所以我直接用 Cloudflare Worker 來佈署應用程式, Cloudflare 與一般把應用程式放到單一伺服器然後前面擋一個 CDN 有非常大的不同, 前者是將你的程式碼直接推到 Cloudflare 的全球邊緣伺服器中,也就是不管什麼時候程式有變更, 所有用戶都可以立即收到更新,而後者是比較一般的作法,如果遇到更新前與更新後檔案名稱相同的, 如dist/index.html
,而這個這個文件有變更,要嘛手動 purge CDN 的檔案, 或者就是需要等到 CDN 的邊緣快取 TTL 到期,用戶才會收到更新。
此外,因為 Cloudflare Worker 也是 Serverless 架構,所以流量的負載全部都交給 Cloudflare 即可。
如果想要知道如何將Web Application佈署至 Cloudflare Worker,可以閱讀我的另一篇文章:
用戶認證
因為不想做後端,只是需要一個登入功能,也不用紀錄當前用戶是誰,所以我也將用戶認證的工作交給了 Cloudflare Access:
不過其實 Cloudflare Access 的初衷不是用來做這件事情的,但是因為剛好符合這次的使用,所以我選用了 Cloudflare Access 來作為登入功能。
Cloudflare Access 的好處是支援 One-Time PIN 以及 Facebook, Google, GitHub 等 OAuth2 登入, 並且只要從後端允許某些電子郵件可以登入,不管用什麼方法登入,只要與允許的 Email Match 之後就可以成功登入。
後記
因為用編輯器直接編輯 JSON 文件來製作曲目列表,實在是太麻煩了,乾脆在寫一個 JSON 曲目列表產生器: