Files
tvbox-Swift/tvbox/Models/AppConfig.swift
T
2026-03-01 14:04:44 +08:00

217 lines
6.4 KiB
Swift

import Foundation
// MARK: - 灵活类型解码器
/// 兼容 JSON 中数值字段可能是字符串 "0" 或整数 0 的情况
/// Android Gson 自动处理此差异,Swift 需要手动兼容
struct FlexibleInt: Codable, Hashable {
let value: Int
init(_ value: Int) {
self.value = value
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let intVal = try? container.decode(Int.self) {
self.value = intVal
} else if let stringVal = try? container.decode(String.self),
let intVal = Int(stringVal) {
self.value = intVal
} else {
self.value = 0
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
/// 兼容 ext 字段可能是字符串或对象
struct FlexibleExt: Codable, Hashable {
let stringValue: String?
let dictValue: [String: AnyCodableValue]?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let str = try? container.decode(String.self) {
self.stringValue = str
self.dictValue = nil
} else if let dict = try? container.decode([String: AnyCodableValue].self) {
self.stringValue = nil
self.dictValue = dict
} else {
self.stringValue = nil
self.dictValue = nil
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let str = stringValue {
try container.encode(str)
} else if let dict = dictValue {
try container.encode(dict)
}
}
}
/// 通用的 JSON 值类型,用于处理混合类型的字典
enum AnyCodableValue: Codable, Hashable {
case string(String)
case int(Int)
case double(Double)
case bool(Bool)
case dict([String: AnyCodableValue])
case array([AnyCodableValue])
case null
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let str = try? container.decode(String.self) {
self = .string(str)
} else if let int = try? container.decode(Int.self) {
self = .int(int)
} else if let double = try? container.decode(Double.self) {
self = .double(double)
} else if let bool = try? container.decode(Bool.self) {
self = .bool(bool)
} else if let dict = try? container.decode([String: AnyCodableValue].self) {
self = .dict(dict)
} else if let array = try? container.decode([AnyCodableValue].self) {
self = .array(array)
} else {
self = .null
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let s): try container.encode(s)
case .int(let i): try container.encode(i)
case .double(let d): try container.encode(d)
case .bool(let b): try container.encode(b)
case .dict(let d): try container.encode(d)
case .array(let a): try container.encode(a)
case .null: try container.encodeNil()
}
}
/// 将可能的任意类型转换为字符串(如果原本是字典或数组,则转为 JSON 字符串)
var stringValue: String? {
switch self {
case .string(let s): return s
case .int(let i): return String(i)
case .double(let d): return String(d)
case .bool(let b): return b ? "true" : "false"
case .null: return nil
case .dict, .array:
if let data = try? JSONEncoder().encode(self),
let str = String(data: data, encoding: .utf8) {
return str
}
return nil
}
}
}
// MARK: - 解析接口配置
/// 解析接口配置 - 对应 Android 版 ParseBean.java
struct ParseBean: Codable, Identifiable, Hashable {
var id: String { name }
var name: String = ""
var url: String = ""
var type: Int = 0 // 0:嗅探 1:解析
var ext: [String: String]? = nil
init(name: String = "", url: String = "", type: Int = 0) {
self.name = name
self.url = url
self.type = type
}
}
// MARK: - API 配置顶层结构
/// API 配置顶层结构 - 对应 JSON 配置文件格式
struct AppConfigData: Codable {
var spider: String?
var wallpaper: String?
var sites: [SiteConfig]?
var parses: [ParseConfig]?
var lives: [LiveConfig]?
var doh: [DoHConfig]?
var rules: [RuleConfig]?
var hosts: [String]?
var flags: [String]?
var ads: [String]?
struct SiteConfig: Codable {
var key: String?
var name: String?
var api: String?
var searchable: FlexibleInt?
var filterable: FlexibleInt?
var quickSearch: FlexibleInt?
var playerType: FlexibleInt?
var type: FlexibleInt?
var ext: AnyCodableValue?
var jar: String?
var style: AnyCodableValue?
var playUrl: String?
var categories: [String]?
var click: String?
enum CodingKeys: String, CodingKey {
case key, name, api, searchable, filterable, quickSearch
case playerType, type, ext, jar, style, playUrl, categories, click
// type_flag 也可能出现
}
}
struct ParseConfig: Codable {
var name: String?
var url: String?
var type: FlexibleInt?
var ext: FlexibleExt?
}
struct LiveConfig: Codable {
var name: String?
var url: String?
var type: FlexibleInt?
var ua: String?
var epg: String?
var logo: String?
var pass: AnyCodableValue? // Android 配置中存在
var playerType: FlexibleInt?
var channels: [LiveChannelConfig]?
struct LiveChannelConfig: Codable {
var name: String?
var urls: [String]?
var group: String?
var logo: String?
}
}
struct DoHConfig: Codable {
var name: String?
var url: String?
}
struct RuleConfig: Codable {
var name: String?
var host: String?
var hosts: [String]?
var rule: [String]?
var regex: [String]?
var filter: [String]?
var script: [String]?
}
}