|
|
|
@ -144,9 +144,7 @@ public class MusicService: NSObject { |
|
|
|
"id": songObj["id"] as? Int64, |
|
|
|
"name": songObj["name"] as? String, |
|
|
|
"singer": songObj["singer"] as? String, |
|
|
|
"url": songObj["url"] as? String, |
|
|
|
"image": songObj["image"] as? String, |
|
|
|
"ts": songObj["ts"] as? Int64 |
|
|
|
]) |
|
|
|
} |
|
|
|
|
|
|
|
@ -154,17 +152,167 @@ public class MusicService: NSObject { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public func play(song: [String: Any]) { |
|
|
|
guard let urlString = song["url"] as? String, let url = URL(string: urlString) else { return } |
|
|
|
// MARK: - 获取歌曲URL |
|
|
|
public func getSongUrl(id: Int64, completion: @escaping (Result<[String: Any?], Error>) -> Void) { |
|
|
|
guard !serviceUrl.isEmpty, let url = URL(string: "\(serviceUrl)/api/home/music_url") else { |
|
|
|
completion(.failure(NSError(domain: "MusicService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Service URL is not configured"]))) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
request.httpMethod = "POST" |
|
|
|
request.addValue(userToken, forHTTPHeaderField: "Authorization") |
|
|
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
request.timeoutInterval = 30.0 |
|
|
|
|
|
|
|
let body: [String: Any] = [ |
|
|
|
"id": id |
|
|
|
] |
|
|
|
|
|
|
|
do { |
|
|
|
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: []) |
|
|
|
} catch { |
|
|
|
completion(.failure(error)) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
print("发送获取URL请求: id=\(id)") |
|
|
|
|
|
|
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in |
|
|
|
if let error = error { |
|
|
|
print("获取URL请求失败: \(error.localizedDescription)") |
|
|
|
completion(.failure(error)) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { |
|
|
|
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 |
|
|
|
print("获取URL失败,HTTP状态码: \(statusCode)") |
|
|
|
completion(.failure(NSError(domain: "MusicService", code: statusCode, userInfo: [NSLocalizedDescriptionKey: "获取URL失败,HTTP状态码: \(statusCode)"]))) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
guard let data = data else { |
|
|
|
print("获取URL响应数据为空") |
|
|
|
completion(.failure(NSError(domain: "MusicService", code: -2, userInfo: [NSLocalizedDescriptionKey: "响应数据为空"]))) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
do { |
|
|
|
if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { |
|
|
|
let urlData = self.parseUrlFromResponse(jsonResponse: jsonResponse) |
|
|
|
print("获取URL成功: \(urlData)") |
|
|
|
completion(.success(urlData)) |
|
|
|
} else { |
|
|
|
completion(.failure(NSError(domain: "MusicService", code: -3, userInfo: [NSLocalizedDescriptionKey: "解析JSON响应失败"]))) |
|
|
|
} |
|
|
|
} catch { |
|
|
|
print("解析获取URL响应时发生异常: \(error.localizedDescription)") |
|
|
|
completion(.failure(error)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
task.resume() |
|
|
|
} |
|
|
|
|
|
|
|
private func parseUrlFromResponse(jsonResponse: [String: Any]) -> [String: Any?] { |
|
|
|
var urlData: [String: Any?] = [:] |
|
|
|
|
|
|
|
guard let data = jsonResponse["data"] as? [String: Any] else { |
|
|
|
return urlData |
|
|
|
} |
|
|
|
|
|
|
|
urlData["id"] = data["id"] as? Int64 |
|
|
|
urlData["url"] = data["url"] as? String |
|
|
|
urlData["ts"] = data["ts"] as? Int64 |
|
|
|
|
|
|
|
return urlData |
|
|
|
} |
|
|
|
|
|
|
|
public func playPlaylist(songs: [[String: Any]]) { |
|
|
|
playlist = songs |
|
|
|
currentIndex = 0 |
|
|
|
if !playlist.isEmpty { |
|
|
|
play(song: playlist[currentIndex]) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public func play(song: [String: Any]) { |
|
|
|
// 获取歌曲ID |
|
|
|
if let id = song["id"] as? NSNumber { |
|
|
|
currentIdInternal = id.int64Value |
|
|
|
let id: Int64 |
|
|
|
if let songId = song["id"] as? NSNumber { |
|
|
|
id = songId.int64Value |
|
|
|
currentIdInternal = id |
|
|
|
} else { |
|
|
|
id = 0 |
|
|
|
currentIdInternal = 0 |
|
|
|
} |
|
|
|
|
|
|
|
print("开始播放: \(urlString), ID: \(currentIdInternal ?? 0)") |
|
|
|
print("开始播放play id: \(id)") |
|
|
|
|
|
|
|
// 检查 URL 和时间戳 |
|
|
|
let url = song["url"] as? String |
|
|
|
let ts = song["ts"] as? Int64 |
|
|
|
let currentTime = Int64(Date().timeIntervalSince1970) // 当前时间戳(秒) |
|
|
|
|
|
|
|
// 判断是否需要重新获取 URL |
|
|
|
let needNewUrl = url?.isEmpty != false || ts == nil || ts! <= currentTime |
|
|
|
|
|
|
|
if needNewUrl { |
|
|
|
print("URL 不存在或已过期,重新获取 URL") |
|
|
|
// 调用 getSongUrl 获取新的 URL |
|
|
|
getSongUrl(id: id) { [weak self] result in |
|
|
|
DispatchQueue.main.async { |
|
|
|
switch result { |
|
|
|
case .success(let urlData): |
|
|
|
if let newUrl = urlData["url"] as? String, !newUrl.isEmpty { |
|
|
|
// 更新 song 数据 |
|
|
|
var updatedSong = song |
|
|
|
updatedSong["url"] = newUrl |
|
|
|
updatedSong["ts"] = urlData["ts"] ?? 0 |
|
|
|
|
|
|
|
// 更新 playlist 中对应的歌曲数据 |
|
|
|
self?.updatePlaylistSong(songId: id, updatedSong: updatedSong) |
|
|
|
|
|
|
|
// 使用新 URL 开始播放 |
|
|
|
self?.startPlayback(song: updatedSong, url: newUrl) |
|
|
|
} else { |
|
|
|
print("获取到的 URL 为空") |
|
|
|
} |
|
|
|
case .failure(let error): |
|
|
|
print("获取 URL 失败: \(error.localizedDescription)") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// URL 有效,直接播放 |
|
|
|
startPlayback(song: song, url: url!) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 新增方法:更新 playlist 中指定 ID 的歌曲数据 |
|
|
|
private func updatePlaylistSong(songId: Int64, updatedSong: [String: Any]) { |
|
|
|
for i in 0..<playlist.count { |
|
|
|
let playlistSong = playlist[i] |
|
|
|
if let playlistSongId = playlistSong["id"] as? NSNumber, |
|
|
|
playlistSongId.int64Value == songId { |
|
|
|
// 找到匹配的歌曲,更新数据 |
|
|
|
playlist[i] = updatedSong |
|
|
|
print("已更新 playlist 中 ID 为 \(songId) 的歌曲数据") |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 新增方法:实际的播放逻辑 |
|
|
|
private func startPlayback(song: [String: Any], url: String) { |
|
|
|
guard let playUrl = URL(string: url) else { |
|
|
|
print("无效的播放URL: \(url)") |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
print("开始播放: \(url), ID: \(currentIdInternal ?? 0)") |
|
|
|
|
|
|
|
// 检查是否已在播放列表中 |
|
|
|
let existingIndex = playlist.firstIndex { existingSong in |
|
|
|
@ -183,7 +331,7 @@ public class MusicService: NSObject { |
|
|
|
} |
|
|
|
|
|
|
|
// 创建播放项 |
|
|
|
let playerItem = AVPlayerItem(url: url) |
|
|
|
let playerItem = AVPlayerItem(url: playUrl) |
|
|
|
player = AVPlayer(playerItem: playerItem) |
|
|
|
|
|
|
|
// 设置播放器观察者 |
|
|
|
@ -198,13 +346,6 @@ public class MusicService: NSObject { |
|
|
|
print("播放器已创建,等待加载完成...") |
|
|
|
} |
|
|
|
|
|
|
|
public func playPlaylist(songs: [[String: Any]]) { |
|
|
|
playlist = songs |
|
|
|
currentIndex = 0 |
|
|
|
if !playlist.isEmpty { |
|
|
|
play(song: playlist[currentIndex]) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public func pause() { |
|
|
|
player?.pause() |
|
|
|
|