BashでSpotify CLIツールを作りたいで：OAuth認証・エラー処理・デバイス管理の工夫

### 初投稿です（　＾ω＾）・・・

## はじめに

Spotifyをターミナルから操作できたら便利だと思い、BashでCLIツールを作成してみました。この記事では、特にOAuth認証の実装、エラー処理、デバイス管理における工夫点を詳しく解説します。

## OAuth認証の実装

SpotifyのWeb APIを利用するためには、OAuth 2.0による認証が必要です。認証フローを理解することが重要なので、まず基本的な流れを説明します。

### OAuth 2.0の基本フロー

1. アプリケーション登録
   - Spotify Developer Dashboardでアプリを登録
   - クライアントIDとシークレットを取得
   - リダイレクトURIを設定（ローカルの場合 `http://localhost:8888/callback`）

2. 認証トークンの取得
   ```bash
   # 認証用URLの生成
   SPOTIFY_AUTH_URL="https://accounts.spotify.com/authorize"
   SCOPE="user-read-playback-state user-modify-playback-state playlist-read-private"
   
   auth_url="${SPOTIFY_AUTH_URL}?client_id=${CLIENT_ID}&response_type=code&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}"
   echo "以下のURLにアクセスして認証を行ってください："
   echo "$auth_url"
   ```

3. アクセストークンの管理
```bash
# トークン管理の実装
store_tokens() {
    local access_token="$1"
    local refresh_token="$2"
    local expires_in="$3"
    
    # トークンの暗号化（OpenSSL使用）
    encrypt_token "$access_token" "${TOKEN_DIR}/access_token"
    encrypt_token "$refresh_token" "${TOKEN_DIR}/refresh_token"
    
    # 有効期限の保存（現在時刻 + expires_in）
    echo $(($(date +%s) + expires_in)) > "${TOKEN_DIR}/expiry"
    
    # ファイルのパーミッション設定
    chmod 600 "${TOKEN_DIR}"/*
}
```

### セキュリティ上の注意点

1. 環境変数の取り扱い
   - クライアントIDとシークレットは必ず環境変数で管理
   - `.env`ファイルはGitの管理対象外に
   - 環境変数ファイルのパーミッションを制限（600）

2. トークンの安全な保存
```bash
# セキュアなトークン保存のための関数
encrypt_token() {
    local token="$1"
    local output_file="$2"
    
    # 暗号化キーの生成（環境変数を利用）
    local key=$(echo -n "${SPOTIFY_CLIENT_SECRET}${SPOTIFY_CLIENT_ID}" | sha256sum | cut -d' ' -f1)
    
    # OpenSSLによる暗号化（AES-256-CBC）
    echo -n "$token" | openssl enc -aes-256-cbc -salt -pbkdf2 \
        -pass "pass:${key}" \
        -out "$output_file"
    
    # ファイルパーミッションの設定
    chmod 600 "$output_file"
}
```

## エラー処理の実装

APIとの通信では様々なエラーが発生する可能性があります。以下、主要なエラーパターンとその対処法を説明します。

### 1. APIリクエストのラッパー関数

```bash
spotify_api_call() {
    local method="$1"
    local endpoint="$2"
    local data="$3"
    local max_retries=3
    local retry_count=0
    local base_wait_time=2
    
    while [ $retry_count -lt $max_retries ]; do
        # アクセストークンの有効性確認
        ensure_valid_token
        
        # APIリクエストの実行
        response=$(curl -s -X "$method" \
            -H "Authorization: Bearer $(get_access_token)" \
            ${data:+-d "$data"} \
            "https://api.spotify.com/v1/$endpoint")
        
        local status_code=$(echo "$response" | jq -r '.error.status // 0')
        
        case $status_code in
            0)  # 成功
                echo "$response"
                return 0
                ;;
            401)  # 認証エラー
                refresh_access_token
                ;;
            429)  # レート制限
                wait_rate_limit "$response"
                ;;
            5*)  # サーバーエラー
                exponential_backoff $retry_count $base_wait_time
                ;;
            *)  # その他のエラー
                log_error "$status_code" "$response"
                return 1
                ;;
        esac
        
        retry_count=$((retry_count + 1))
    done
    
    echo "最大リトライ回数（${max_retries}回）を超えました" >&2
    return 1
}
```

### 2. レート制限への対応

Spotifyでは1時間あたり約1000リクエストの制限があります。これを超えると429エラーが返されます。

```bash
wait_rate_limit() {
    local response="$1"
    local retry_after=$(echo "$response" | jq -r '.error.retry_after // 5')
    
    echo "レート制限に達しました。${retry_after}秒待機します..." >&2
    sleep "$retry_after"
}
```

### 3. エラーログの実装

```bash
log_error() {
    local status_code="$1"
    local response="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message=$(echo "$response" | jq -r '.error.message // "Unknown error"')
    
    printf "[%s] Error %d: %s\n" "$timestamp" "$status_code" "$message" >> "${LOG_DIR}/error.log"
}
```

## デバイス管理とUX設計

CLIツールの使いやすさを考慮し、以下のような工夫を施しました。

### 1. インタラクティブなデバイス選択

```bash
select_device() {
    local devices=$(spotify_api_call "GET" "me/player/devices")
    
    # デバイス一覧の表示
    echo "利用可能なデバイス:"
    echo "$devices" | jq -r '.devices[] | "\(.id): \(.name) (\(.type))\(.is_active?" [現在アクティブ]":"")"' | \
        awk '{printf "%3d: %s\n", NR, $0}'
    
    # 前回使用したデバイスの提案
    local last_device=$(get_last_device)
    if [ -n "$last_device" ]; then
        local device_name=$(echo "$devices" | jq -r ".devices[] | select(.id == \"$last_device\") | .name")
        if [ -n "$device_name" ]; then
            read -p "前回使用したデバイス($device_name)を使用しますか？ [Y/n] " use_last
            [[ "$use_last" =~ ^[Yy]?$ ]] && echo "$last_device" && return 0
        fi
    fi
    
    # デバイスの選択
    while true; do
        read -p "デバイス番号を選択してください: " device_number
        if [[ "$device_number" =~ ^[0-9]+$ ]]; then
            local selected_id=$(echo "$devices" | jq -r ".devices[$(($device_number-1))].id")
            if [ -n "$selected_id" ]; then
                save_last_device "$selected_id"
                echo "$selected_id"
                return 0
            fi
        fi
        echo "無効な選択です。もう一度試してください。" >&2
    done
}
```

### 2. 直感的なコマンド体系

CLIのコマンド設計は、一貫性と使いやすさを重視しました：

```bash
# メインコマンド
spotify-cli COMMAND [OPTIONS]

# サブコマンド一覧
play [URI]          # 再生（URIは省略可能）
pause               # 一時停止
next                # 次の曲
prev                # 前の曲
vol [0-100]         # 音量調整
device              # デバイス選択
status              # 現在の再生状況

# オプション
--device, -d ID     # デバイスを指定
--verbose, -v       # 詳細出力
--quiet, -q         # 出力を最小限に
```

### 3. シェルエイリアスの提案

```bash
# ~/.bashrcや~/.zshrcに追加するエイリアス
alias sp="spotify-cli"
alias sp-play="spotify-cli play"
alias sp-pause="spotify-cli pause"
alias sp-next="spotify-cli next"
alias sp-prev="spotify-cli prev"
alias sp-vol="spotify-cli vol"

# 音量調整のショートカット
sp-vol-up() { spotify-cli vol $(($(spotify-cli vol-get) + 10)) }
sp-vol-down() { spotify-cli vol $(($(spotify-cli vol-get) - 10)) }
```

## 今後の改善計画

1. よく聴く曲の統計情報
   - Spotify APIの`/me/top/tracks`エンドポイントを使用
   - 期間別（短期・中期・長期）の再生回数を集計
   - ジャンルやアーティスト別の分析グラフ生成

2. プレイリストの検索・管理
   - あいまい検索の実装（fzfとの連携）
   - プレイリストのエクスポート/インポート機能
   - プレイリストの自動バックアップ

3. 楽曲の詳細情報表示
   - BPM、キー、時間などの音楽的特徴の表示
   - 類似曲のレコメンド機能
   - 歌詞の表示（可能な場合）

## まとめ

BashでSpotify CLIツールを作成する過程で、以下の点が特に勉強になりました：

1. OAuth認証の実装方法と安全なトークン管理
2. APIエラーへの適切な対処方法
3. CLIツールのUX設計における重要なポイント



## 参考文献

1. [Spotify Web API Reference](https://developer.spotify.com/documentation/web-api/reference/)
2. [OAuth 2.0 RFC](https://tools.ietf.org/html/rfc6749)
3. [The Art of Unix Programming](http://www.catb.org/~esr/writings/taoup/html/)
4. [Command Line Interface Guidelines](https://clig.dev/)