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

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

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

Spotify は最近、パソコンとウェブ プレーヤー クライアント向けに Spotify Miniplayer をリリースしました。ミニプレーヤーは、必要不可欠な再生コントロールを、常に最前面に表示される小さなコンパクトなウィンドウにまとめ、ユーザーが Spotify に常にアクセスできるように設計されています。ユーザーからの要望が長らく寄せられていたこの機能により、ユーザーは Spotify で好きなアーティスト、プレイリスト、ポッドキャストを楽しみながら、さまざまなウィンドウやアプリでシームレスにマルチタスクを行うことができます。

以下では、最初の「キャンバス ハック」から、新しい Document Picture-in-Picture API をベースにした、より高度でユーザー フレンドリーなバージョンまで、ミニプレーヤーの開発について詳しく説明します。

「キャンバス ハック」

ミニプレーヤーの最初のバージョンは、2019 年に Spotify のウェブ プレーヤーでハック プロジェクトとしてリリースされました。ブラウザの <video> のピクチャー イン ピクチャー(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 は、任意の HTML コンテンツを PiP ウィンドウ内に表示できる新しい 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 は、独自の外観とプレーヤーのコントロールを組み込むことができ、ユーザー エクスペリエンスを向上させました。
  • Spotify は、Document PiP API のサポートを Spotify のデスクトップ クライアントに導入し、数百万人のデスクトップ ユーザーにミニプレーヤーを提供できるようになりました。

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

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

次の例は、Spotify チームと同様に、React でドキュメントのピクチャー イン ピクチャーを使用する方法を示しています。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 が提供する機会に注目し、この API のサポートを検討してくれることを願っています。これにより、Spotify は、選択したブラウザに関係なく、すべてのユーザーに一貫したエクスペリエンスを提供できるようになります。

謝辞

ミニプレーヤーの開発に携わった Spotify のすべての方々に感謝いたします。

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