Files
suixinkan_uikit/suixinkan/Features/Live/Models/LiveModels.swift

371 lines
11 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// LiveModels.swift
// suixinkan
//
import Foundation
/// Android `AliveEntity.status`
enum LiveStatus: Int, Sendable, Equatable {
case pending = 1
case living = 2
case paused = 3
case finished = 4
case unknown = 0
///
var isOperable: Bool { self != .finished }
}
/// Android `manualPushMode`
enum LivePushMode: Int, CaseIterable, Sendable, Equatable {
case clarityFirst = 1
case smoothFirst = 2
///
var title: String {
switch self {
case .clarityFirst: "清晰度优先"
case .smoothFirst: "流畅度优先"
}
}
}
/// Android `AliveEntity`
struct LiveItem: Decodable, Sendable, Equatable, Hashable, Identifiable {
let id: Int
let title: String
let coverImg: String
let pushUrl: String
let startTime: Int64
let endTime: Int64
let duration: Int64
let status: Int
let statusLabel: String
let manualPushMode: Int
let manualPushState: Int
let viewsCount: Int
enum CodingKeys: String, CodingKey {
case id
case title
case coverImg = "cover_img"
case pushUrl = "push_url"
case startTime = "start_time"
case endTime = "end_time"
case duration
case status
case statusLabel = "status_label"
case manualPushMode = "manual_push_mode"
case manualPushState = "manual_push_state"
case legacyManualPushMode = "manaul_push_mode"
case legacyManualPushState = "manaul_push_state"
case viewsCount = "views_count"
}
init(
id: Int = 0,
title: String = "",
coverImg: String = "",
pushUrl: String = "",
startTime: Int64 = 0,
endTime: Int64 = 0,
duration: Int64 = 0,
status: Int = 0,
statusLabel: String = "",
manualPushMode: Int = 1,
manualPushState: Int = 0,
viewsCount: Int = 0
) {
self.id = id
self.title = title
self.coverImg = coverImg
self.pushUrl = pushUrl
self.startTime = startTime
self.endTime = endTime
self.duration = duration
self.status = status
self.statusLabel = statusLabel
self.manualPushMode = manualPushMode
self.manualPushState = manualPushState
self.viewsCount = viewsCount
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeLossyInt(forKey: .id) ?? 0
title = try container.decodeLossyString(forKey: .title)
coverImg = try container.decodeLossyString(forKey: .coverImg).unescapedSlashes
pushUrl = try container.decodeLossyString(forKey: .pushUrl).unescapedSlashes
startTime = try container.decodeLossyInt64(forKey: .startTime) ?? 0
endTime = try container.decodeLossyInt64(forKey: .endTime) ?? 0
duration = try container.decodeLossyInt64(forKey: .duration) ?? 0
status = try container.decodeLossyInt(forKey: .status) ?? 0
statusLabel = try container.decodeLossyString(forKey: .statusLabel)
manualPushMode = try container.decodeLossyInt(forKey: .manualPushMode)
?? container.decodeLossyInt(forKey: .legacyManualPushMode)
?? 1
manualPushState = try container.decodeLossyInt(forKey: .manualPushState)
?? container.decodeLossyInt(forKey: .legacyManualPushState)
?? 0
viewsCount = try container.decodeLossyInt(forKey: .viewsCount) ?? 0
}
///
var liveStatus: LiveStatus { LiveStatus(rawValue: status) ?? .unknown }
///
var displayTitle: String { title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "暂无标题" : title }
///
var controlTitle: String { liveStatus == .living ? "暂停" : "开始" }
}
/// Android `ItemListResponse<AliveEntity>`
struct LiveListResponse: Decodable, Sendable, Equatable {
let page: Int
let pageSize: Int
let total: Int
let totalPages: Int
let items: [LiveItem]
enum CodingKeys: String, CodingKey {
case page
case pageSize = "page_size"
case total
case totalPages = "total_pages"
case items
}
init(page: Int = 1, pageSize: Int = 10, total: Int = 0, totalPages: Int = 0, items: [LiveItem] = []) {
self.page = page
self.pageSize = pageSize
self.total = total
self.totalPages = totalPages
self.items = items
}
}
///
struct LiveCreateRequest: Encodable, Sendable, Equatable {
let scenicId: String
let title: String
let coverImg: String
enum CodingKeys: String, CodingKey {
case scenicId = "scenic_id"
case title
case coverImg = "cover_img"
}
}
///
struct LiveControlRequest: Encodable, Sendable, Equatable {
let liveId: Int
enum CodingKeys: String, CodingKey {
case liveId = "live_id"
}
}
///
struct LivePushModeRequest: Encodable, Sendable, Equatable {
let liveId: Int
let manualPushMode: Int
enum CodingKeys: String, CodingKey {
case liveId = "live_id"
case manualPushMode = "manual_push_mode"
}
}
/// Android `AliveAlbumFileEntity`
struct LiveAlbumFile: Codable, Sendable, Equatable, Hashable, Identifiable {
let url: String
let type: Int
let size: Int64
let coverImg: String?
let id: Int
enum CodingKeys: String, CodingKey {
case url
case type
case size
case coverImg = "cover_img"
case id
}
init(url: String, type: Int, size: Int64, coverImg: String? = nil, id: Int = 0) {
self.url = url.unescapedSlashes
self.type = type
self.size = size
self.coverImg = coverImg?.unescapedSlashes
self.id = id
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
url = try container.decodeLossyString(forKey: .url).unescapedSlashes
type = try container.decodeLossyInt(forKey: .type) ?? 2
size = try container.decodeLossyInt64(forKey: .size) ?? 0
let cover = try container.decodeIfPresent(String.self, forKey: .coverImg)
coverImg = cover?.unescapedSlashes
id = try container.decodeLossyInt(forKey: .id) ?? 0
}
}
/// Android `AliveAlbumFolderEntity`
struct LiveAlbumFolder: Decodable, Sendable, Equatable, Hashable, Identifiable {
let id: Int
let albumId: Int
let name: String
let creator: String
let items: [LiveAlbumFile]
enum CodingKeys: String, CodingKey {
case id
case albumId = "album_id"
case name
case creator
case items
}
init(id: Int = 0, albumId: Int = 0, name: String = "", creator: String = "", items: [LiveAlbumFile] = []) {
self.id = id
self.albumId = albumId
self.name = name
self.creator = creator
self.items = items
}
///
var displayName: String { name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "暂无标题" : name }
}
///
struct LiveAlbumFolderListResponse: Decodable, Sendable, Equatable {
let items: [LiveAlbumFolder]
let page: Int
let pageSize: Int
let total: Int
let totalPages: Int
enum CodingKeys: String, CodingKey {
case items
case page
case pageSize = "page_size"
case total
case totalPages = "total_pages"
}
init(items: [LiveAlbumFolder] = [], page: Int = 1, pageSize: Int = 10, total: Int = 0, totalPages: Int = 0) {
self.items = items
self.page = page
self.pageSize = pageSize
self.total = total
self.totalPages = totalPages
}
}
///
struct LiveAlbumFolderCreateRequest: Encodable, Sendable, Equatable {
let scenicId: String
let name: String
let items: [LiveAlbumFile]
enum CodingKeys: String, CodingKey {
case scenicId = "scenic_id"
case name
case items
}
}
///
struct LiveAlbumFolderDeleteRequest: Encodable, Sendable, Equatable {
let folderId: Int
enum CodingKeys: String, CodingKey {
case folderId = "folder_id"
}
}
///
struct LiveAlbumFileDeleteRequest: Encodable, Sendable, Equatable {
let folderId: Int
let fileIds: [Int]
enum CodingKeys: String, CodingKey {
case folderId = "folder_id"
case fileIds = "file_ids"
}
}
///
struct LivePickedMedia: Sendable, Equatable {
let data: Data
let fileName: String
let size: Int64
let width: Int
let height: Int
}
private extension KeyedDecodingContainer {
func decodeLossyString(forKey key: Key) throws -> String {
if let value = try? decodeIfPresent(String.self, forKey: key) {
return value
}
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return String(value)
}
if let value = try? decodeIfPresent(Int64.self, forKey: key) {
return String(value)
}
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return String(value)
}
return ""
}
func decodeLossyInt(forKey key: Key) throws -> Int? {
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return value
}
if let value = try? decodeIfPresent(Int64.self, forKey: key) {
return Int(value)
}
if let value = try? decodeIfPresent(String.self, forKey: key) {
let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
if let intValue = Int(text) { return intValue }
if let doubleValue = Double(text) { return Int(doubleValue) }
}
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return Int(value)
}
return nil
}
func decodeLossyInt64(forKey key: Key) throws -> Int64? {
if let value = try? decodeIfPresent(Int64.self, forKey: key) {
return value
}
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return Int64(value)
}
if let value = try? decodeIfPresent(String.self, forKey: key) {
let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
if let intValue = Int64(text) { return intValue }
if let doubleValue = Double(text) { return Int64(doubleValue) }
}
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return Int64(value)
}
return nil
}
}
private extension String {
var unescapedSlashes: String {
replacingOccurrences(of: "\\/", with: "/")
}
}