Skip to content

Commit

Permalink
Merge pull request #431 from Darock-Studio/fix/download
Browse files Browse the repository at this point in the history
revert: Reverted background download due to post-process problem. Fix…
  • Loading branch information
WindowsMEMZ authored Nov 30, 2024
2 parents 94a01c5 + 18ebace commit 68aaccd
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 110 deletions.
76 changes: 49 additions & 27 deletions MeowBili/PersonalCenter/DownloadsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import MarqueeText
import AVFoundation
import SDWebImageSwiftUI

var downloadingProgressDataList = [(pts: PassthroughSubject<DownloadTaskDetailData, Never>, isFinished: Bool)]()
var failedDownloadTasks = [Int]()
var downloadResumeDatas = [Int: [String: String]]()
var videoDownloadRequests = [DownloadRequest]()

struct DownloadsView: View {
public static var willPlayVideoPath = ""
@State var metadatas = [[String: String]]()
Expand Down Expand Up @@ -140,15 +145,15 @@ struct DownloadingListView: View {
@State var downloadProgresses = [Double]()
@State var downloadedSizes = [Int64]()
@State var totalSizes = [Int64]()
@State var localFailedDownloadTasks = Set<Int>()
@State var videoDetails = [[String: String]]()
@State var localFailedDownloadTasks = [Int]()
@State var localVariableUpdateTimer: Timer?
@State var isRefreshing = false
var body: some View {
List {
if !videoDownloadRequests.isEmpty && !isRefreshing {
ForEach(0..<videoDownloadRequests.count, id: \.self) { i in
if !localFailedDownloadTasks.contains(videoDownloadRequests[i].taskIdentifier) {
if downloadProgresses[from: i] != 1.0 {
if downloadingProgressDataList.count != 0 && totalSizes.count != 0 {
ForEach(0..<downloadingProgressDataList.count, id: \.self) { i in
if !localFailedDownloadTasks.contains(i) {
if !(downloadingProgressDataList[from: i]?.isFinished ?? false) && downloadProgresses[from: i] != 1.0 {
if let downloadProgress = downloadProgresses[from: i], let downloadSize = downloadedSizes[from: i], let totalSize = totalSizes[from: i] {
VStack {
HStack {
Expand All @@ -157,7 +162,7 @@ struct DownloadingListView: View {
.bold()
Spacer()
}
MarqueeText(text: videoDownloadDetails[videoDownloadRequests[i].taskIdentifier]?["Title"] ?? "", font: .systemFont(ofSize: 17), leftFade: 5, rightFade: 5, startDelay: 1.5)
MarqueeText(text: videoDetails[i]["Title"] ?? "", font: .systemFont(ofSize: 17), leftFade: 5, rightFade: 5, startDelay: 1.5)
ProgressView(value: downloadProgress * 100, total: 100.0)
HStack {
Spacer()
Expand All @@ -180,10 +185,11 @@ struct DownloadingListView: View {
Image(systemName: "xmark.circle.fill")
})
}
.onReceive(videoDownloadRequests[i].progress.publisher(for: \.completedUnitCount)) { _ in
downloadProgresses[i] = videoDownloadRequests[i].progress.fractionCompleted
downloadedSizes[i] = videoDownloadRequests[i].progress.completedUnitCount
totalSizes[i] = videoDownloadRequests[i].progress.totalUnitCount
.onReceive(downloadingProgressDataList[i].pts) { data in
downloadProgresses[i] = data.data.progress
downloadedSizes[i] = data.data.currentSize
totalSizes[i] = data.data.totalSize
videoDetails[i] = data.videoDetails
}
}
} else {
Expand All @@ -200,16 +206,34 @@ struct DownloadingListView: View {
}
} else {
Button(action: {
if let resumeData = try? Data(contentsOf: URL(filePath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(videoDownloadRequests[i].taskIdentifier).drkdatav")) {
let task = videoDownloadSession.downloadTask(withResumeData: resumeData)
if let sourceDetails = videoDownloadDetails[videoDownloadRequests[i].taskIdentifier] {
videoDownloadDetails.removeValue(forKey: videoDownloadRequests[i].taskIdentifier)
videoDownloadDetails.updateValue(sourceDetails, forKey: task.taskIdentifier)
DispatchQueue(label: "com.darock.DarockBili.VideoDownload", qos: .background).async {
if let resumeData = try? Data(contentsOf: URL(filePath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(i).drkdatav")) {
failedDownloadTasks.remove(at: i)
AF.download(resumingWith: resumeData)
.downloadProgress { p in
downloadingProgressDataList[i].pts.send(.init(data: .init(progress: p.fractionCompleted, currentSize: p.completedUnitCount, totalSize: p.totalUnitCount), videoDetails: downloadResumeDatas[i]!))
}
.response { r in
if r.error == nil, let filePath = r.fileURL?.path {
debugPrint(filePath)
debugPrint(downloadResumeDatas[i]!["BV"] ?? "")
var detTmp = downloadResumeDatas[i] ?? [:]
detTmp.updateValue(filePath, forKey: "Path")
detTmp.updateValue(String(Date.now.timeIntervalSince1970), forKey: "Time")
let setKey = detTmp["SetKey"] ?? downloadResumeDatas[i]!["BV"]!
detTmp.removeValue(forKey: "SetKey")
UserDefaults.standard.set(detTmp, forKey: setKey)
downloadingProgressDataList[i].isFinished = true
} else {
if FileManager.default.fileExists(atPath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(i).drkdatav") {
try? FileManager.default.removeItem(atPath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(i).drkdatav")
}
try? r.resumeData?.write(to: URL(filePath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(i).drkdatav"))
failedDownloadTasks.append(i)
debugPrint(r.error as Any)
}
}
}
task.resume()
videoDownloadNeedsResumeIdentifiers.remove(videoDownloadRequests[i].taskIdentifier)
videoDownloadRequests[i] = task
isRefreshing = true
}
}, label: {
VStack {
Expand All @@ -231,22 +255,20 @@ struct DownloadingListView: View {
}
} else {
Text("Download.nothing")
.onAppear {
isRefreshing = false
}
}
}
.navigationTitle("Download.list")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
downloadProgresses = Array(repeating: 0.0, count: videoDownloadRequests.count)
downloadedSizes = Array(repeating: 0, count: videoDownloadRequests.count)
totalSizes = Array(repeating: 0, count: videoDownloadRequests.count)
downloadProgresses = Array(repeating: 0.0, count: downloadingProgressDataList.count)
downloadedSizes = Array(repeating: 0, count: downloadingProgressDataList.count)
totalSizes = Array(repeating: 0, count: downloadingProgressDataList.count)
videoDetails = Array(repeating: [:], count: downloadingProgressDataList.count)
#if os(watchOS)
Dynamic.PUICApplication.sharedPUICApplication().setExtendedIdleTime(3600.0, disablesSleepGesture: true, wantsAutorotation: false)
#endif
localVariableUpdateTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
localFailedDownloadTasks = videoDownloadNeedsResumeIdentifiers
localFailedDownloadTasks = failedDownloadTasks
}
}
.onDisappear {
Expand Down
2 changes: 1 addition & 1 deletion MeowBili/Video/VideoDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ struct VideoDetailView: View {
}
Spacer()
.frame(height: 20)
MarqueeText(text: videoDetails["Title"]!, font: .systemFont(ofSize: 12, weight: .bold), leftFade: 5, rightFade: 5, startDelay: 1.5)
MarqueeText(text: videoDetails["Title"]!, font: .systemFont(ofSize: 12, weight: .bold), leftFade: 5, rightFade: 5, startDelay: 1.5, alignment: .center)
.padding(.horizontal, 10)
Text(videoDetails["UP"]!)
.lineLimit(1)
Expand Down
108 changes: 37 additions & 71 deletions MeowBili/Video/VideoDownloadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ import Dynamic
import DarockKit
import Alamofire

var videoDownloadNeedsResumeIdentifiers = Set<Int>()
var videoDownloadRequests = [URLSessionTask]()
var videoDownloadDetails = [Int: [String: String]]()
let videoDownloadSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.darock.DarockBili.Video-Download.background")
configuration.isDiscretionary = false
configuration.sessionSendsLaunchEvents = true
let session = URLSession(configuration: configuration, delegate: VideoDownloadSessionDelegate.shared, delegateQueue: nil)
return session
}()

struct VideoDownloadView: View {
var bvid: String
var videoDetails: [String: String]
Expand Down Expand Up @@ -85,11 +74,11 @@ struct VideoDownloadView: View {
AF.request("https://api.bilibili.com/x/web-interface/view?bvid=\(bvid)").response { response in
let cid = Int64((String(data: response.data!, encoding: .utf8)?.components(separatedBy: "\"pages\":[{\"cid\":")[1].components(separatedBy: ",")[0])!)!
AF.request("https://api.bilibili.com/x/player/playurl?platform=html5&bvid=\(bvid)&cid=\(cid)", headers: headers).response { response in
let headers = [
let headers: HTTPHeaders = [
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"cookie": "SESSDATA=\(sessdata)",
"cookie": "",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.30 Safari/537.36 Edg/84.0.522.11",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
Expand All @@ -99,68 +88,45 @@ struct VideoDownloadView: View {
"referer": "https://www.bilibili.com/"
]
let downloadCID = String(VideoDownloadView.downloadCID ?? 0)
isLoading = false
var request = URLRequest(url: URL(string: VideoDownloadView.downloadLink ?? (String(data: response.data!, encoding: .utf8)?.components(separatedBy: ",\"url\":\"")[1].components(separatedBy: "\",")[0])!.replacingOccurrences(of: "\\u0026", with: "&"))!)
request.allHTTPHeaderFields = headers
let task = videoDownloadSession.downloadTask(with: request)
task.resume()
var videoDetails = videoDetails
if isPaged {
videoDetails.updateValue(downloadCID, forKey: "DownloadCID")
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("dlds/\(bvid)\(isPaged ? downloadCID : "").mp4")

return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
videoDownloadDetails.updateValue(videoDetails, forKey: task.taskIdentifier)
videoDownloadRequests.append(task)
isLoading = false
downloadingProgressDataList.append((.init(), false))
let currentDownloadingIndex = downloadingProgressDataList.count - 1
videoDownloadRequests.append(
AF.download((VideoDownloadView.downloadLink ?? (String(data: response.data!, encoding: .utf8)?.components(separatedBy: ",\"url\":\"")[1].components(separatedBy: "\",")[0])!.replacingOccurrences(of: "\\u0026", with: "&")), headers: headers, to: destination)
.downloadProgress { p in
downloadingProgressDataList[currentDownloadingIndex].pts.send(.init(data: .init(progress: p.fractionCompleted, currentSize: p.completedUnitCount, totalSize: p.totalUnitCount), videoDetails: videoDetails))
}
.response { r in
if r.error == nil, let filePath = r.fileURL?.path {
debugPrint(filePath)
debugPrint(bvid)
var detTmp = videoDetails
detTmp.updateValue(filePath, forKey: "Path")
detTmp.updateValue(String(Date.now.timeIntervalSince1970), forKey: "Time")
UserDefaults.standard.set(detTmp, forKey: "\(bvid)\(isPaged ? downloadCID : "")")
downloadingProgressDataList[currentDownloadingIndex].isFinished = true
} else {
if FileManager.default.fileExists(atPath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(currentDownloadingIndex).drkdatav") {
try? FileManager.default.removeItem(atPath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(currentDownloadingIndex).drkdatav")
}
try? r.resumeData?.write(to: URL(filePath: NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(currentDownloadingIndex).drkdatav"))
var detTmp = videoDetails
detTmp.updateValue("\(bvid)\(isPaged ? String(VideoDownloadView.downloadCID!) : "")", forKey: "SetKey")
downloadResumeDatas.updateValue(detTmp, forKey: currentDownloadingIndex)
failedDownloadTasks.append(currentDownloadingIndex)
debugPrint(r.error as Any)
}
}
)
VideoDownloadView.downloadLink = nil
}
}
}
}
}

private class VideoDownloadSessionDelegate: NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static let shared = VideoDownloadSessionDelegate()

func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
debugPrint("Finish Background Events")
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
debugPrint(location)
downloadTask.progress.completedUnitCount = downloadTask.progress.totalUnitCount
if var details = videoDownloadDetails[downloadTask.taskIdentifier] {
let destination = URL(filePath: NSHomeDirectory() + "/Documents/dlds/\(details["BV"]!)\(details["DownloadCID"] ?? "").mp4")
do {
try FileManager.default.moveItem(at: location, to: destination)
details.updateValue(destination.path, forKey: "Path")
details.updateValue(String(Date.now.timeIntervalSince1970), forKey: "Time")
UserDefaults.standard.set(details, forKey: "\(details["BV"]!)\(details["DownloadCID"] ?? "")")
} catch {
print(error)
}
}
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
DispatchQueue.main.async {
downloadTask.progress.completedUnitCount = totalBytesWritten
downloadTask.progress.totalUnitCount = totalBytesExpectedToWrite
}
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: (any Error)?) {
guard let error else { return }
let userInfo = (error as NSError).userInfo
if let resumeData = userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
do {
let resumeFilePath = NSHomeDirectory() + "/tmp/VideoDownloadResumeData\(task.taskIdentifier).drkdatav"
if FileManager.default.fileExists(atPath: resumeFilePath) {
try FileManager.default.removeItem(atPath: resumeFilePath)
}
try resumeData.write(to: URL(filePath: resumeFilePath))
videoDownloadNeedsResumeIdentifiers.insert(task.taskIdentifier)
} catch {
print(error)
}
}
}
}
Loading

0 comments on commit 68aaccd

Please sign in to comment.