- Add OAuth 2.0 PKCE authentication for Spotify Web API - Create SpotifyNowPlayingMonitor for polling current track - Add Settings tab with music source toggle (Apple Music/Spotify) - Store tokens securely in Keychain - Display current track on Glass as NOW_PLAYING card 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
92 lines
1.9 KiB
Swift
92 lines
1.9 KiB
Swift
//
|
|
// SpotifyModels.swift
|
|
// iris
|
|
//
|
|
// Codable models for Spotify Web API responses.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
struct SpotifyTokens: Codable {
|
|
let accessToken: String
|
|
let refreshToken: String
|
|
let expiresAt: Date
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case accessToken = "access_token"
|
|
case refreshToken = "refresh_token"
|
|
case expiresAt = "expires_at"
|
|
}
|
|
|
|
var isExpired: Bool {
|
|
Date() >= expiresAt
|
|
}
|
|
|
|
var expiresWithinMinutes: Bool {
|
|
Date().addingTimeInterval(5 * 60) >= expiresAt
|
|
}
|
|
}
|
|
|
|
struct SpotifyTokenResponse: Codable {
|
|
let accessToken: String
|
|
let tokenType: String
|
|
let expiresIn: Int
|
|
let refreshToken: String?
|
|
let scope: String?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case accessToken = "access_token"
|
|
case tokenType = "token_type"
|
|
case expiresIn = "expires_in"
|
|
case refreshToken = "refresh_token"
|
|
case scope
|
|
}
|
|
}
|
|
|
|
struct SpotifyPlaybackState: Codable {
|
|
let isPlaying: Bool
|
|
let progressMs: Int?
|
|
let item: SpotifyTrack?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case isPlaying = "is_playing"
|
|
case progressMs = "progress_ms"
|
|
case item
|
|
}
|
|
}
|
|
|
|
struct SpotifyTrack: Codable {
|
|
let id: String
|
|
let name: String
|
|
let artists: [SpotifyArtist]
|
|
let album: SpotifyAlbum
|
|
let durationMs: Int
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id, name, artists, album
|
|
case durationMs = "duration_ms"
|
|
}
|
|
}
|
|
|
|
struct SpotifyArtist: Codable {
|
|
let id: String
|
|
let name: String
|
|
}
|
|
|
|
struct SpotifyAlbum: Codable {
|
|
let id: String
|
|
let name: String
|
|
}
|
|
|
|
enum MusicSource: String, CaseIterable, Codable {
|
|
case appleMusic = "apple_music"
|
|
case spotify = "spotify"
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .appleMusic: return "Apple Music"
|
|
case .spotify: return "Spotify"
|
|
}
|
|
}
|
|
}
|