import Foundation public struct DeviceInfo: Equatable { public let allspace: UInt64 public let freespace: UInt64 public let name: String public let size: String public let brand: String public let powerlevel: Int /// 播放模式(旧固件无此字段时为 nil) public let loop: PlayMode? } extension DeviceInfo: Codable { enum CodingKeys: String, CodingKey { case allspace, freespace, name, size, brand, powerlevel, loop } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // All string fields: fallback to "" if missing name = (try? container.decode(String.self, forKey: .name)) ?? "" size = (try? container.decode(String.self, forKey: .size)) ?? "" brand = (try? container.decode(String.self, forKey: .brand)) ?? "" // powerlevel: Int, may come as Int or String if let num = try? container.decode(Int.self, forKey: .powerlevel) { powerlevel = num } else if let str = try? container.decode(String.self, forKey: .powerlevel), let num = Int(str) { powerlevel = num } else { powerlevel = 0 } // allspace/freespace: may come as number, string, or be missing allspace = Self.decodeFlexibleUInt64(container: container, key: .allspace) freespace = Self.decodeFlexibleUInt64(container: container, key: .freespace) // loop: optional. Accept Int 0/1; anything else → nil. if let raw = try? container.decode(Int.self, forKey: .loop), let mode = PlayMode(rawValue: raw) { loop = mode } else { loop = nil } } private static func decodeFlexibleUInt64( container: KeyedDecodingContainer, key: CodingKeys ) -> UInt64 { if let value = try? container.decode(UInt64.self, forKey: key) { return value } if let value = try? container.decode(Int.self, forKey: key) { return UInt64(max(0, value)) } if let str = try? container.decode(String.self, forKey: key), let value = UInt64(str) { return value } return 0 } }