mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 23:54:32 +00:00
Still not broken: Next commit decides the fate
This commit is contained in:
parent
cb2afda0f2
commit
dc45c50df4
17 changed files with 1028 additions and 608 deletions
|
|
@ -0,0 +1,157 @@
|
|||
//
|
||||
// YouTubeMusicModels.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Created by Alexander on 2025-09-14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Configuration
|
||||
struct YouTubeMusicConfiguration: Sendable {
|
||||
let baseURL: String
|
||||
let bundleIdentifier: String
|
||||
let reconnectDelay: ClosedRange<TimeInterval>
|
||||
let updateInterval: TimeInterval
|
||||
|
||||
static let `default` = YouTubeMusicConfiguration(
|
||||
baseURL: "http://localhost:26538",
|
||||
bundleIdentifier: "com.github.th-ch.youtube-music",
|
||||
reconnectDelay: 1...60,
|
||||
updateInterval: 2.0
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - API Models
|
||||
struct AuthResponse: Decodable, Sendable {
|
||||
let accessToken: String
|
||||
}
|
||||
|
||||
struct PlaybackResponse: Decodable, Sendable {
|
||||
let isPaused: Bool
|
||||
let title: String?
|
||||
let artist: String?
|
||||
let album: String?
|
||||
let elapsedSeconds: Double?
|
||||
let songDuration: Double?
|
||||
let imageSrc: String?
|
||||
let repeatMode: Int?
|
||||
let isShuffled: Bool?
|
||||
}
|
||||
|
||||
// MARK: - WebSocket Message Types
|
||||
enum WebSocketMessageType: String, Sendable {
|
||||
case playerInfo = "PLAYER_INFO"
|
||||
case videoChanged = "VIDEO_CHANGED"
|
||||
case playerStateChanged = "PLAYER_STATE_CHANGED"
|
||||
case positionChanged = "POSITION_CHANGED"
|
||||
case volumeChanged = "VOLUME_CHANGED"
|
||||
case repeatChanged = "REPEAT_CHANGED"
|
||||
case shuffleChanged = "SHUFFLE_CHANGED"
|
||||
}
|
||||
|
||||
struct WebSocketMessage {
|
||||
let type: WebSocketMessageType
|
||||
let rawData: Data
|
||||
private let parsedJSON: [String: Any]?
|
||||
|
||||
init?(from data: Data) {
|
||||
let json = (try? JSONSerialization.jsonObject(with: data) as? [String: Any])
|
||||
guard let typeString = json?["type"] as? String,
|
||||
let messageType = WebSocketMessageType(rawValue: typeString) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.type = messageType
|
||||
self.rawData = data
|
||||
self.parsedJSON = json
|
||||
}
|
||||
|
||||
func extractData() -> [String: Any]? {
|
||||
parsedJSON
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Extensions
|
||||
extension PlaybackResponse {
|
||||
static func from(websocketData: [String: Any]) -> PlaybackResponse? {
|
||||
let songData = websocketData["song"] as? [String: Any]
|
||||
|
||||
let isPaused: Bool
|
||||
if let paused = songData?["isPaused"] as? Bool {
|
||||
isPaused = paused
|
||||
} else if let playing = websocketData["isPlaying"] as? Bool {
|
||||
isPaused = !playing
|
||||
} else {
|
||||
isPaused = true
|
||||
}
|
||||
|
||||
let title = (songData?["title"] as? String) ??
|
||||
(songData?["alternativeTitle"] as? String) ??
|
||||
(websocketData["title"] as? String)
|
||||
let artist = (songData?["artist"] as? String) ?? (websocketData["artist"] as? String)
|
||||
let album = songData?["album"] as? String
|
||||
|
||||
let elapsed = extractDouble(from: songData, key: "elapsedSeconds") ??
|
||||
extractDouble(from: websocketData, key: "position")
|
||||
|
||||
let duration = extractDouble(from: songData, key: "songDuration") ??
|
||||
extractDouble(from: websocketData, key: "songDuration")
|
||||
|
||||
let imageSrc = (songData?["imageSrc"] as? String) ?? (websocketData["imageSrc"] as? String)
|
||||
let isShuffled = (websocketData["shuffle"] as? Bool) ?? (songData?["isShuffled"] as? Bool)
|
||||
|
||||
var repeatModeInt: Int? = nil
|
||||
if let repeatVal = websocketData["repeat"] as? String {
|
||||
switch repeatVal.uppercased() {
|
||||
case "NONE": repeatModeInt = 0
|
||||
case "ALL": repeatModeInt = 1
|
||||
case "ONE": repeatModeInt = 2
|
||||
default: break
|
||||
}
|
||||
} else if let repeatStr = songData?["repeat"] as? String {
|
||||
switch repeatStr.uppercased() {
|
||||
case "NONE": repeatModeInt = 0
|
||||
case "ALL": repeatModeInt = 1
|
||||
case "ONE": repeatModeInt = 2
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
return PlaybackResponse(
|
||||
isPaused: isPaused,
|
||||
title: title,
|
||||
artist: artist,
|
||||
album: album,
|
||||
elapsedSeconds: elapsed,
|
||||
songDuration: duration,
|
||||
imageSrc: imageSrc,
|
||||
repeatMode: repeatModeInt,
|
||||
isShuffled: isShuffled
|
||||
)
|
||||
}
|
||||
|
||||
func with(elapsedSeconds: Double) -> PlaybackResponse {
|
||||
PlaybackResponse(
|
||||
isPaused: isPaused,
|
||||
title: title,
|
||||
artist: artist,
|
||||
album: album,
|
||||
elapsedSeconds: elapsedSeconds,
|
||||
songDuration: songDuration,
|
||||
imageSrc: imageSrc,
|
||||
repeatMode: repeatMode,
|
||||
isShuffled: isShuffled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func extractDouble(from dict: [String: Any]?, key: String) -> Double? {
|
||||
guard let dict = dict else { return nil }
|
||||
if let value = dict[key] as? Double {
|
||||
return value
|
||||
} else if let value = dict[key] as? Int {
|
||||
return Double(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue