Add Spotify integration with toggleable music source
- 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>
This commit is contained in:
91
IrisCompanion/iris/Spotify/SpotifyModels.swift
Normal file
91
IrisCompanion/iris/Spotify/SpotifyModels.swift
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user