Spotify が Picture-in-Picture API を使用して Spotify ミニプレーヤーを開発した方法

Guido Kessels
Guido Kessels
François Beaufort
François Beaufort

世界で最も人気のオーディオ ストリーミング サブスクリプション サービスである Spotify は、ユーザーによるオーディオや動画コンテンツの視聴方法を継続的に改善することを目指しています。音楽、ポッドキャスト、オーディオブックの豊富なライブラリを提供し、モバイル、PC、その他のプラットフォームを使用して、毎日何百万人ものユーザーに対応します。

Spotify は最近、デスクトップ プレーヤーとウェブ プレーヤー クライアント向けに Spotify ミニプレーヤーをリリースしました。ミニプレーヤーは、重要な再生コントロールを小さな、コンパクトなウィンドウで常に表示できるように設計されており、ユーザーは Spotify にいつでもアクセスできます。かねてからご要望の多かったこの機能を使用することで、Spotify でお気に入りのアーティスト、プレイリスト、ポッドキャストを楽しみながら、さまざまなウィンドウやアプリでシームレスにマルチタスクを行えるようになります。

ここでは、ミニプレーヤーの開発を、最初の「キャンバス ハック」から、新しい Document Picture-in-Picture API 上に構築された、より高度でユーザー フレンドリーなバージョンまで、詳しく見ていきましょう。

「キャンバスのハッキング」

ミニプレーヤーの最初のイテレーションは、2019 年にハッキング プロジェクトとして Spotify のウェブ プレーヤーでリリースされました。目標は、ブラウザの <video> 用 Picture-in-Picture(PIP)API を使用して、アルバムアートを常に前面に表示することでした。ただし、この API は主に動画要素用に設計されており、アルバムアートの画像を表示することはできませんでした。Spotify は、アルバムアートをキャンバス要素にレンダリングし、HTMLCanvasElement captureStream() メソッドを使用してリアルタイムの MediaStream オブジェクトを取得することで、これを回避しました。このストリームは PiP API に使用される動画のソースとして機能します。このアプローチは、Google Chrome の「オーディオ プレイリスト」サンプルに基づいています。

Spotify はキャンバスを Media Session API で設定された適切なアクション ハンドラと組み合わせて、PIP ウィンドウに表示するプレーヤー コントロールを制御しました。これにより、アルバムアートやプレーヤー コントロールを含むフローティング ウィンドウが実現し、再生をコントロールしながら他のタスクに集中できるようになりました。

Spotify の基本ミニプレーヤー ウィンドウのスクリーンショット。

これにより、Spotify は基本的なミニプレーヤーを使用することができました。ただし、この方法にはいくつかの制限事項がありました。

  • PIP ウィンドウ内では動画の字幕はサポートされていません。Spotify ではすべての動画に字幕を表示することが義務付けられていたため、動画の再生が始まるとすぐに PIP ウィンドウを強制的に閉じていました。
  • プレーヤー コントロールは、再生がローカルで行われる場合にのみ表示されます。Spotify は、Spotify Connect(およびその他のプロトコル)を使用したリモート再生を許可しており、ユーザーがこの再生を操作できるようにする必要があります
  • PIP ウィンドウのデザインのカスタマイズはサポートされていません。Spotify はアートワークを表示し、Chrome が提供するプレーヤー コントロールしか使用できないため、Spotify のブランディングや追加のプレーヤー コントロールを追加できなくなります。

ユーザー インターフェースを制御できず、Spotify 固有の機能を追加できないこと(トラックの高評価など)は、このアプローチがデスクトップ クライアントに適していないと感じられていたことを意味します。

ドキュメントのピクチャー イン ピクチャー: ミニプレーヤーの進化

2023 年初頭、Spotify は、PIP ウィンドウ内に任意の HTML コンテンツを表示できる新しい API(Document Picture-in-Picture API)のリリースに Google Chrome が再び関心を寄せていることを知りました。この開発は、Spotify が PIP ウィンドウの外観を完全に制御できるようになるという点で、非常に画期的なものでした。Spotify はオリジン トライアルで Chrome チームと共同で、Document Picture-in-Picture API をベースに構築された新しいミニプレーヤーを開発しました。

Document PiP API を使用すると、常に前面に表示される新しいウィンドウを開き、要素をアタッチできます。Spotify ウェブ プレーヤーは React ウェブ アプリケーションであるため、Spotify は ReactDOM の createPortal() メソッドを使用してカスタム コンポーネントをメイン アプリケーションから PIP ウィンドウにレンダリングし、ミニプレーヤーの外観と機能を完全に制御できるようにしました。

新しい Document Picture-in-Picture API は、Spotify の以前の問題にも対処しています。

  • PIP ウィンドウ内の動画は通常の動画要素であり、字幕を完全にサポートしています。
  • UI を完全に制御することで、Spotify Connect を使用してリモートで再生している場合でも、プレーヤー コントロールを表示できます。
  • Spotify はデザインとプレーヤー コントロールを組み込んで、ユーザー エクスペリエンスを向上させることができました。
  • Document PiP API のサポートを Spotify のデスクトップ クライアントに提供することで、数百万人のデスクトップ ユーザーがミニプレーヤーを利用できるようにしました。

Spotify の新しいミニプレーヤー ウィンドウのスクリーンショット。

React を使用してピクチャー イン ピクチャー ウィンドウを作成する

次の例は、Spotify チームと同じように、React で Document Picture-in-Picture を使用する方法を示しています。MyFeaturePiPContainer の 2 つの React コンポーネントを作成します。

MyFeature コンポーネントは、ピクチャー イン ピクチャー ウィンドウを管理します。ピクチャー イン ピクチャー ウィンドウを切り替えるボタンと、PiPContainer コンポーネントをレンダリングするボタンをレンダリングします。また、ピクチャー イン ピクチャー ウィンドウの "pagehide" イベントに登録して、ウィンドウが閉じられたときに状態を更新します。

const MyFeature = () => {
  const [pipWindow, setPiPWindow] = useState<Window | null>(
    documentPictureInPicture.window
  );

  const handleClick = useCallback(async () => {
    if (pipWindow) {
      pipWindow.close();
    } else {
      const newWindow = await documentPictureInPicture.requestWindow();
      setPiPWindow(newWindow);
    }
  }, [pipWindow]);

  useEffect(() => {
    const handleWindowClose = (): void => {
      setPiPWindow(null);
    };

    pipWindow?.addEventListener("pagehide", handleWindowClose);

    return () => {
      pipWindow?.removeEventListener("pagehide", handleWindowClose);
    };
  }, [pipWindow]);

  return (
    <>
      <button onClick={handleClick}>
        {pipWindow ? "Close PiP Window" : "Open PiP Window"}
      </button>
      <PiPContainer pipWindow={pipWindow}>Hello World 👋!</PiPContainer>
    </>
  );
};

PiPContainer コンポーネントは、ReactDOM の createPortal() メソッドを使用してコンテンツをピクチャー イン ピクチャー ウィンドウにレンダリングします。

type Props = PropsWithChildren<{
  pipWindow: Window | null;
}>;

const PiPContainer = ({ pipWindow, children }: Props) => {
  useEffect(() => {
    if (pipWindow) {
      cloneStyles(window.document, pipWindow.document);
    }
  }, [pipWindow]);

  return pipWindow ? createPortal(children, pipWindow.document.body) : null;
};

次のステップ

Spotify は進化と革新を続けながら、ミニプレーヤーの強化に引き続きコミットし、機能とユーザー エクスペリエンスのさらなる改善を計画しています。まだ特定の機能にコミットすることはできませんが、ミニプレーヤーの今後の可能性に期待を寄せています。

Spotify ミニプレーヤー ウィンドウのさまざまな形状のスクリーンショット。

Document Picture-in-Picture API により、柔軟性と制御性が向上し、より直感的でユーザー フレンドリーなミニプレーヤーを作成できます。他のブラウザ ベンダーの方にも、この API がもたらす機会を取り入れて、サポートの導入を検討されることをおすすめいたします。これにより、Spotify は、選択したブラウザに関係なく、すべてのユーザーに一貫性のある優れたエクスペリエンスを提供できるようになります。

謝辞

ミニプレーヤーの開発に関わった Spotify の皆さん、ありがとうございました。

また、Spotify は Google Chrome チームの協力と、Document Picture-in-Picture API に関する Spotify のフィードバックを参考にしていただいたことに感謝します。