feat: 视频列表增加 checkbox 选择列和全选按钮,修复 PlayInfo 解码失败导致下载面板无选项的问题
CI / lint-and-build (push) Has been cancelled
CI / lint-and-build (push) Has been cancelled
- PlayInfo 添加 CodingKeys 映射 PascalCase JSON 字段(PlayURL/Definition/Duration/Size/Format)\n- Table 第一列增加圆形 checkbox 图标,点击可切换选中状态\n- 增加全选/取消全选按钮\n- prepareDownload 增加错误提示\n- 下载面板无播放地址时显示提示文字
This commit is contained in:
@@ -131,6 +131,14 @@ struct PlayInfo: Codable, Equatable {
|
||||
var duration: String?
|
||||
var size: Int64?
|
||||
var format: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case playURL = "PlayURL"
|
||||
case definition = "Definition"
|
||||
case duration = "Duration"
|
||||
case size = "Size"
|
||||
case format = "Format"
|
||||
}
|
||||
}
|
||||
|
||||
struct CategoryItem: Identifiable, Codable, Equatable {
|
||||
|
||||
@@ -37,15 +37,20 @@ struct DownloadPanelView: View {
|
||||
Text(item.video.title)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
Picker("清晰度", selection: Binding(
|
||||
get: { selectedDefinitions[item.video.videoId] ?? item.playInfos.first?.definition ?? "" },
|
||||
set: { selectedDefinitions[item.video.videoId] = $0 }
|
||||
)) {
|
||||
ForEach(item.playInfos, id: \.definition) { info in
|
||||
Text("\(info.definition) - \(formatSize(info.size))").tag(info.definition)
|
||||
if item.playInfos.isEmpty {
|
||||
Text("无可用播放地址")
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Picker("清晰度", selection: Binding(
|
||||
get: { selectedDefinitions[item.video.videoId] ?? item.playInfos.first?.definition ?? "" },
|
||||
set: { selectedDefinitions[item.video.videoId] = $0 }
|
||||
)) {
|
||||
ForEach(item.playInfos, id: \.definition) { info in
|
||||
Text("\(info.definition) - \(formatSize(info.size))").tag(info.definition)
|
||||
}
|
||||
}
|
||||
.frame(width: 200)
|
||||
}
|
||||
.frame(width: 200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,14 @@ struct VideoListView: View {
|
||||
}
|
||||
.disabled(selectedVideos.isEmpty || isLoading)
|
||||
|
||||
Button("全选") {
|
||||
if selectedVideos.count == videos.count {
|
||||
selectedVideos.removeAll()
|
||||
} else {
|
||||
selectedVideos = Set(videos.map(\.videoId))
|
||||
}
|
||||
}
|
||||
|
||||
Button("批量删除") {
|
||||
Task { await batchDelete() }
|
||||
}
|
||||
@@ -65,6 +73,19 @@ struct VideoListView: View {
|
||||
}
|
||||
|
||||
Table(of: VideoItem.self, selection: $selectedVideos) {
|
||||
TableColumn("") { video in
|
||||
Image(systemName: selectedVideos.contains(video.videoId) ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundStyle(selectedVideos.contains(video.videoId) ? .blue : .secondary)
|
||||
.onTapGesture {
|
||||
if selectedVideos.contains(video.videoId) {
|
||||
selectedVideos.remove(video.videoId)
|
||||
} else {
|
||||
selectedVideos.insert(video.videoId)
|
||||
}
|
||||
}
|
||||
}
|
||||
.width(min: 30, ideal: 30, max: 30)
|
||||
|
||||
TableColumn("标题") { video in
|
||||
HStack {
|
||||
AsyncImage(url: video.coverURL.flatMap(URL.init)) { image in
|
||||
@@ -172,15 +193,24 @@ struct VideoListView: View {
|
||||
|
||||
private func prepareDownload() async {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
var results: [(video: VideoItem, playInfos: [PlayInfo])] = []
|
||||
var errors: [String] = []
|
||||
for video in videos where selectedVideos.contains(video.videoId) {
|
||||
do {
|
||||
let response = try await VODClient.shared.getPlayInfo(videoId: video.videoId)
|
||||
results.append((video, response.playInfos))
|
||||
if response.playInfos.isEmpty {
|
||||
errors.append("「\(video.title)」无可用播放地址")
|
||||
}
|
||||
} catch {
|
||||
results.append((video, []))
|
||||
errors.append("「\(video.title)」获取播放信息失败:\(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
if !errors.isEmpty {
|
||||
errorMessage = errors.joined(separator: "\n")
|
||||
}
|
||||
downloadResults = results
|
||||
showDownloadPanel = true
|
||||
isLoading = false
|
||||
|
||||
Reference in New Issue
Block a user