init: init proj
This commit is contained in:
32
DDYSClient/Models/ContentCategory.swift
Normal file
32
DDYSClient/Models/ContentCategory.swift
Normal file
@@ -0,0 +1,32 @@
|
||||
import Foundation
|
||||
|
||||
enum ContentCategory: String, CaseIterable, Identifiable, Codable {
|
||||
case movie = "movie"
|
||||
case series = "series"
|
||||
case variety = "variety"
|
||||
case anime = "anime"
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .movie: return "电影"
|
||||
case .series: return "电视剧"
|
||||
case .variety: return "综艺"
|
||||
case .anime: return "动漫"
|
||||
}
|
||||
}
|
||||
|
||||
var pathPrefix: String {
|
||||
"/\(rawValue)"
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .movie: return "film"
|
||||
case .series: return "tv"
|
||||
case .variety: return "theatermasks"
|
||||
case .anime: return "sparkles"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
DDYSClient/Models/ContentDetail.swift
Normal file
12
DDYSClient/Models/ContentDetail.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
import Foundation
|
||||
|
||||
struct ContentDetail {
|
||||
let item: ContentItem
|
||||
let description: String
|
||||
let directors: [String]
|
||||
let actors: [String]
|
||||
let genres: [String]
|
||||
let region: String
|
||||
let sources: [StreamSource]
|
||||
let episodes: [Episode]?
|
||||
}
|
||||
22
DDYSClient/Models/ContentItem.swift
Normal file
22
DDYSClient/Models/ContentItem.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import Foundation
|
||||
|
||||
struct ContentItem: Identifiable, Hashable, Codable {
|
||||
let id: String // slug 作为 ID
|
||||
let title: String
|
||||
let year: Int
|
||||
let category: ContentCategory
|
||||
let rating: Double?
|
||||
let posterURL: URL?
|
||||
let badges: [String] // 新片/已完结/热门/推荐
|
||||
let onlineCount: Int
|
||||
let netdiskCount: Int
|
||||
let detailURL: String // 相对路径如 /movie/slug
|
||||
|
||||
static func == (lhs: ContentItem, rhs: ContentItem) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
7
DDYSClient/Models/Episode.swift
Normal file
7
DDYSClient/Models/Episode.swift
Normal file
@@ -0,0 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
struct Episode: Identifiable, Codable, Hashable {
|
||||
let id: Int
|
||||
let name: String // "第01集"
|
||||
let url: String // m3u8 地址
|
||||
}
|
||||
93
DDYSClient/Models/FilterOption.swift
Normal file
93
DDYSClient/Models/FilterOption.swift
Normal file
@@ -0,0 +1,93 @@
|
||||
import Foundation
|
||||
|
||||
struct FilterOption: Identifiable, Hashable {
|
||||
let id: String
|
||||
let name: String
|
||||
let value: String
|
||||
}
|
||||
|
||||
enum FilterType: String, CaseIterable {
|
||||
case sort
|
||||
case genre
|
||||
case region
|
||||
case year
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .sort: return "排序"
|
||||
case .genre: return "类型"
|
||||
case .region: return "地区"
|
||||
case .year: return "年份"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FilterState {
|
||||
var sort: String = ""
|
||||
var genre: String = ""
|
||||
var region: String = ""
|
||||
var year: String = ""
|
||||
|
||||
func buildPath(base: String) -> String {
|
||||
var path = base
|
||||
if !sort.isEmpty { path = "/\(sort)\(base)" }
|
||||
if !genre.isEmpty { path += "/genre/\(genre)" }
|
||||
if !region.isEmpty { path += "/region/\(region)" }
|
||||
if !year.isEmpty { path += "/year/\(year)" }
|
||||
return path
|
||||
}
|
||||
|
||||
static let defaultSorts: [FilterOption] = [
|
||||
FilterOption(id: "sort_latest", name: "最新更新", value: ""),
|
||||
FilterOption(id: "sort_rating", name: "豆瓣评分", value: "rating"),
|
||||
FilterOption(id: "sort_popular", name: "近期热门", value: "popular"),
|
||||
]
|
||||
|
||||
static let defaultGenres: [FilterOption] = [
|
||||
FilterOption(id: "genre_all", name: "全部", value: ""),
|
||||
FilterOption(id: "genre_action", name: "动作", value: "action"),
|
||||
FilterOption(id: "genre_comedy", name: "喜剧", value: "comedy"),
|
||||
FilterOption(id: "genre_drama", name: "剧情", value: "drama"),
|
||||
FilterOption(id: "genre_scifi", name: "科幻", value: "scifi"),
|
||||
FilterOption(id: "genre_horror", name: "恐怖", value: "horror"),
|
||||
FilterOption(id: "genre_romance", name: "爱情", value: "romance"),
|
||||
FilterOption(id: "genre_thriller", name: "惊悚", value: "thriller"),
|
||||
FilterOption(id: "genre_suspense", name: "悬疑", value: "suspense"),
|
||||
FilterOption(id: "genre_adventure", name: "冒险", value: "adventure"),
|
||||
FilterOption(id: "genre_war", name: "战争", value: "war"),
|
||||
FilterOption(id: "genre_history", name: "历史", value: "history"),
|
||||
FilterOption(id: "genre_crime", name: "犯罪", value: "crime"),
|
||||
FilterOption(id: "genre_fantasy", name: "奇幻", value: "fantasy"),
|
||||
FilterOption(id: "genre_animation", name: "动画", value: "animation"),
|
||||
FilterOption(id: "genre_documentary", name: "纪录片", value: "documentary"),
|
||||
FilterOption(id: "genre_family", name: "家庭", value: "family"),
|
||||
FilterOption(id: "genre_music", name: "音乐", value: "music"),
|
||||
FilterOption(id: "genre_sport", name: "运动", value: "sport"),
|
||||
FilterOption(id: "genre_costume", name: "古装", value: "costume"),
|
||||
FilterOption(id: "genre_martial", name: "武侠", value: "martial"),
|
||||
]
|
||||
|
||||
static let defaultRegions: [FilterOption] = [
|
||||
FilterOption(id: "region_all", name: "全部", value: ""),
|
||||
FilterOption(id: "region_usa", name: "美国", value: "usa"),
|
||||
FilterOption(id: "region_uk", name: "英国", value: "uk"),
|
||||
FilterOption(id: "region_korea", name: "韩国", value: "korea"),
|
||||
FilterOption(id: "region_japan", name: "日本", value: "japan"),
|
||||
FilterOption(id: "region_china", name: "中国大陆", value: "china"),
|
||||
FilterOption(id: "region_hongkong", name: "中国香港", value: "hongkong"),
|
||||
FilterOption(id: "region_taiwan", name: "中国台湾", value: "taiwan"),
|
||||
FilterOption(id: "region_france", name: "法国", value: "france"),
|
||||
FilterOption(id: "region_germany", name: "德国", value: "germany"),
|
||||
FilterOption(id: "region_india", name: "印度", value: "india"),
|
||||
FilterOption(id: "region_thailand", name: "泰国", value: "thailand"),
|
||||
]
|
||||
|
||||
static let defaultYears: [FilterOption] = {
|
||||
var options = [FilterOption(id: "year_all", name: "全部", value: "")]
|
||||
let currentYear = Calendar.current.component(.year, from: Date())
|
||||
for year in stride(from: currentYear, through: 2018, by: -1) {
|
||||
options.append(FilterOption(id: "year_\(year)", name: "\(year)", value: "\(year)"))
|
||||
}
|
||||
return options
|
||||
}()
|
||||
}
|
||||
8
DDYSClient/Models/StreamSource.swift
Normal file
8
DDYSClient/Models/StreamSource.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
import Foundation
|
||||
|
||||
struct StreamSource: Identifiable, Codable {
|
||||
let id: Int
|
||||
let name: String // "播放源 1"
|
||||
let quality: String // "1080P"
|
||||
let episodes: [Episode]
|
||||
}
|
||||
18
DDYSClient/Models/VideoPlayerData.swift
Normal file
18
DDYSClient/Models/VideoPlayerData.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
import Foundation
|
||||
|
||||
struct VideoPlayerData: Codable, Hashable, Identifiable {
|
||||
let url: String
|
||||
let title: String
|
||||
let episodeName: String?
|
||||
let contentId: String
|
||||
let episodeId: Int
|
||||
|
||||
var id: String { "\(contentId)-\(episodeId)" }
|
||||
|
||||
var windowTitle: String {
|
||||
if let episodeName {
|
||||
return "\(title) - \(episodeName)"
|
||||
}
|
||||
return title
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user