feat: resolve AniList episodes through ZokoAnime
This commit is contained in:
@@ -241,6 +241,7 @@ final class PlayerModel: ObservableObject {
|
||||
private let youtubeTitleResolver: @MainActor (URL) async throws -> String?
|
||||
private let sponsorResolver: @MainActor (String) async throws -> [SponsorSegment]
|
||||
private let miruroResolver: @MainActor (String, Int) async throws -> MiruroSources
|
||||
private let zokoResolver: @MainActor (String, Int) async throws -> ResolvedSource
|
||||
private let miruroMetadataResolver: @MainActor (String, Int) async throws -> MiruroMetadata
|
||||
private var reportingErrorMessage: String {
|
||||
reportingErrorBuffer.withUnsafeBufferPointer { String(cString: $0.baseAddress!) }
|
||||
@@ -252,6 +253,7 @@ final class PlayerModel: ObservableObject {
|
||||
youtubeTitleResolver: @escaping @MainActor (URL) async throws -> String? = { try await YouTubeResolver.resolveTitle($0) },
|
||||
sponsorResolver: @escaping @MainActor (String) async throws -> [SponsorSegment] = { try await SponsorBlockResolver.resolve(videoID: $0) },
|
||||
miruroResolver: @escaping @MainActor (String, Int) async throws -> MiruroSources = { try await MiruroResolver.resolve(mediaID: $0, episode: $1) },
|
||||
zokoResolver: @escaping @MainActor (String, Int) async throws -> ResolvedSource = { try await ZokoResolver.resolve(mediaID: $0, episode: $1) },
|
||||
miruroMetadataResolver: @escaping @MainActor (String, Int) async throws -> MiruroMetadata = { try await MiruroMetadataResolver.resolve(mediaID: $0, episode: $1) },
|
||||
qualityClock: @escaping () -> Double = { ProcessInfo.processInfo.systemUptime },
|
||||
skipClock: @escaping () -> Double = { ProcessInfo.processInfo.systemUptime }) {
|
||||
@@ -261,6 +263,7 @@ final class PlayerModel: ObservableObject {
|
||||
self.youtubeTitleResolver = youtubeTitleResolver
|
||||
self.sponsorResolver = sponsorResolver
|
||||
self.miruroResolver = miruroResolver
|
||||
self.zokoResolver = zokoResolver
|
||||
self.miruroMetadataResolver = miruroMetadataResolver
|
||||
keyBindings = configuration.keys
|
||||
compactTitleProgress = configuration.compactTitleProgress
|
||||
@@ -441,7 +444,7 @@ final class PlayerModel: ObservableObject {
|
||||
let url: URL
|
||||
switch stream {
|
||||
case .youtube(let canonical): url = canonical
|
||||
case .miruro: url = URL(string: text.trimmingCharacters(in: .whitespacesAndNewlines))!
|
||||
case .miruro, .anilist: url = URL(string: text.trimmingCharacters(in: .whitespacesAndNewlines))!
|
||||
}
|
||||
queue.replaceStreams([(url: url, label: url.absoluteString)])
|
||||
resolveStream(stream, change: .open)
|
||||
@@ -461,6 +464,12 @@ final class PlayerModel: ObservableObject {
|
||||
url = canonical
|
||||
title = video.title
|
||||
source = .youtube(video, height: nil)
|
||||
case .anilist(let mediaID, let episode):
|
||||
let resolved = try await MiruroTransport.prepare(zokoResolver(mediaID, episode))
|
||||
guard resolved.identity == "anilist:\(mediaID):\(episode)" else { throw CocoaError(.fileReadCorruptFile) }
|
||||
url = URL(string: text.trimmingCharacters(in: .whitespacesAndNewlines))!
|
||||
title = resolved.title
|
||||
source = .resolved(resolved)
|
||||
case .miruro(let mediaID, let episode):
|
||||
let providers = try await miruroResolver(mediaID, episode)
|
||||
let selected = try providers.select(videoProvider: miruroVideoProvider,
|
||||
@@ -522,6 +531,7 @@ final class PlayerModel: ObservableObject {
|
||||
switch stream {
|
||||
case .youtube(let url): return url.absoluteString
|
||||
case .miruro(let id, let episode): return "miruro:\(id):\(episode)"
|
||||
case .anilist(let id, let episode): return "anilist:\(id):\(episode)"
|
||||
}
|
||||
}
|
||||
return item.url.absoluteString
|
||||
@@ -562,6 +572,7 @@ final class PlayerModel: ObservableObject {
|
||||
switch stream {
|
||||
case .youtube(let url): resolveYouTube(url, height: nil, change: change)
|
||||
case .miruro(let mediaID, let episode): resolveMiruro(mediaID: mediaID, episode: episode, change: change)
|
||||
case .anilist(let mediaID, let episode): resolveZoko(mediaID: mediaID, episode: episode, change: change)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,6 +693,40 @@ final class PlayerModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveZoko(mediaID: String, episode: Int, change: MediaChange) {
|
||||
guard let handle else { return }
|
||||
if castDevice != nil { stopCasting(resume: false) }
|
||||
cancelStreamResolution()
|
||||
automaticQuality = AutomaticQuality()
|
||||
guard player_pause(handle) else {
|
||||
commandError = "Could not pause the current media to open the stream. Please try again."
|
||||
revealControls()
|
||||
return
|
||||
}
|
||||
objectWillChange.send()
|
||||
let request = transition.beginResolution(change)
|
||||
systemPlayback?.update()
|
||||
commandError = nil
|
||||
revealControls()
|
||||
let resolver = zokoResolver
|
||||
let task = Task { [weak self] in
|
||||
do {
|
||||
let source = try await MiruroTransport.prepare(resolver(mediaID, episode))
|
||||
guard source.identity == "anilist:\(mediaID):\(episode)" else { throw ZokoError.malformed }
|
||||
guard !Task.isCancelled, let self else { return }
|
||||
self.objectWillChange.send()
|
||||
guard let change = self.transition.finishResolution(request) else { return }
|
||||
self.openMedia(.resolved(source), change: change)
|
||||
} catch {
|
||||
guard !Task.isCancelled, let self else { return }
|
||||
self.objectWillChange.send()
|
||||
guard self.transition.finishResolution(request) != nil else { return }
|
||||
self.failStreamChange(change, error: error)
|
||||
}
|
||||
}
|
||||
transition.attachResolution(task, request: request)
|
||||
}
|
||||
|
||||
private func resolveMiruro(mediaID: String, episode: Int, change: MediaChange = .open) {
|
||||
guard let handle else { return }
|
||||
if castDevice != nil { stopCasting(resume: false) }
|
||||
@@ -826,6 +871,8 @@ final class PlayerModel: ObservableObject {
|
||||
title = try await youtube(url)
|
||||
case .miruro(let id, let episode):
|
||||
title = try await miruro(id, episode).title
|
||||
case .anilist(let id, let episode):
|
||||
title = try await ZokoResolver.title(mediaID: id, episode: episode)
|
||||
}
|
||||
} catch {
|
||||
// Optional enrichment must not surface as a playback error or retry every poll.
|
||||
|
||||
Reference in New Issue
Block a user