🎵

Tauri上でSpotifyの再生中の曲情報をリアルタイムで取得し表示する

2024/08/12に公開

導入

今回は、Tauriを使用してSpotifyから再生中の曲情報をリアルタイムで取得する方法を紹介します。環境はmacOSです、悪しからず。
フロントのフレームワークはReactです。

AppleScriptを使ったSpotifyトラック情報の取得

macOSでは、Spotify APIを使わずともAppleScriptを使用してSpotifyから現在再生中の曲情報を取得することができます。以下のようなAppleScriptを作成し、Rustのコードから呼び出します。

tell application "Spotify"
    if player state is playing then
        set trackName to name of current track
        set artistName to artist of current track
        return trackName & " by " & artistName
    else
        return "Spotify is not playing."
    end if
end tell

AppleScript普段書かないので勉強になりました。

  • tell application "Spotify": これはSpotifyアプリケーションに対してコマンドを実行することを示します。
  • if player state is playing then: 現在Spotifyで音楽が再生中かどうかを確認します。
  • set trackName to name of current track: 現在再生中のトラックの名前を取得します。
  • set artistName to artist of current track: 現在再生中のトラックのアーティスト名を取得します。
  • return trackName & " by " & artistName: トラック名とアーティスト名を結合して返します。
  • else return "Spotify is not playing.": 音楽が再生されていない場合のメッセージを返します。

Spotifyが再生中かどうかをチェックし、再生中であればその曲のタイトルとアーティスト名を取得します

これをRustから実行するために、以下のコードを実装します。
名前はspotify_info.rsとでもしておきましょう。

get_spotify_track_info関数を実装します。

spotify_info.rs
use std::process::Command;

#[tauri::command]
pub fn get_spotify_track_info() -> Result<String, String> {
    let script = r#"
    tell application "Spotify"
        if player state is playing then
            set trackName to name of current track
            set artistName to artist of current track
            return trackName & " by " & artistName
        else
            return "Spotify is not playing."
        end if
    end tell
    "#;

    let output = Command::new("osascript")
        .arg("-e")
        .arg(script)
        .output()
        .map_err(|e| e.to_string())?;

    if output.status.success() {
        let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
        Ok(result)
    } else {
        Err(String::from_utf8_lossy(&output.stderr).trim().to_string())
    }
}
  • let script = r#"...";: ここでは、AppleScriptのコードをRustの文字列として定義しています。r#"...";の形式を使うことで、文字列内に特別なエスケープシーケンスなしで"などを含めることができます。
  • Command::new("osascript").arg("-e").arg(script).output(): これはosascriptコマンドを呼び出してAppleScriptを実行し、その結果を取得します。osascriptはmacOSのコマンドで、AppleScriptを実行するために使用されます。
  • 試験的に取得できるかターミナルで叩く場合osascript -e 'tell application "Spotify" to name of current track & " by " & artist of current track'みたいなコマンドです。
  • .map_err(|e| e.to_string())?: コマンドの実行が失敗した場合、そのエラーを文字列に変換して返します。
  • if output.status.success(): コマンドが成功したかどうかをチェックします。成功した場合は、出力をUTF-8として読み取り、その結果を返します。失敗した場合は、標準エラー出力を取得してエラーメッセージとして返します。

コマンドを実装

次に、TauriでAppleScriptを実行するコマンドを実装します。main.rsに新しいコマンドを追加し、Reactコンポーネントから呼び出せるように設定します。

main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

mod system_info;
mod window_events;
mod spotify_info; //追加

use system_info::get_system_info;
use window_events::setup_window_event_listeners;
use spotify_info::get_spotify_track_info; //追加
use tauri::Manager;

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            let window = app.get_window("main").unwrap();
            setup_window_event_listeners(window);
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![get_system_info, get_spotify_track_info]) //ここに追加
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Reactを使ったリアルタイム更新

Reactコンポーネントを作成し、Spotifyの曲情報を定期的に取得するロジックを実装します。ポーリングで、数秒ごとに情報を更新します。

App.tsx
const [spotityTrackInfo, setSpotityTrackInfo] = useState<string>("");

const fetchSpotifyTrackInfo = async () => {
    try {
      const trackInfo: string = await invoke("get_spotify_track_info");
      setSpotityTrackInfo(trackInfo);
    } catch (error) {
      console.error("Spotifyの情報を取得できません:", error);
    }
  };

  useEffect(() => {
    fetchSpotifyTrackInfo();

    const intervalId = setInterval(fetchSpotifyTrackInfo, 5000); // 5秒ごとに更新

    return () => clearInterval(intervalId);
  }, []);

...

<Marquee gradient={false} speed={40} delay={2}>
    {"♪"} &nbsp; {spotityTrackInfo}
</Marquee>

感想

  • Tauri上でのフロントとネイティブコマンドの実行の相性が良すぎるし、簡単
  • Linux、Windowsは知らん

Discussion