init: init proj

This commit is contained in:
2026-02-26 22:15:35 +08:00
commit 7ef5348f65
43 changed files with 3085 additions and 0 deletions

View 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"
}
}
}

View 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]?
}

View 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)
}
}

View File

@@ -0,0 +1,7 @@
import Foundation
struct Episode: Identifiable, Codable, Hashable {
let id: Int
let name: String // "01"
let url: String // m3u8
}

View 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
}()
}

View 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]
}

View 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
}
}