diff --git a/Package.swift b/Package.swift index 93297b1..c189138 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/YouTubeKit/Extensions/Concurrency.swift b/Sources/YouTubeKit/Extensions/Concurrency.swift index 2a85e39..9bfdabe 100644 --- a/Sources/YouTubeKit/Extensions/Concurrency.swift +++ b/Sources/YouTubeKit/Extensions/Concurrency.swift @@ -10,7 +10,7 @@ import Foundation @available(iOS 13.0, watchOS 6.0, tvOS 13.0, macOS 10.15, *) extension Sequence { - func asyncMap(_ transform: (Element) async throws -> T) async rethrows -> [T] { + func asyncMap(_ transform: (Element) async throws -> T, isolation: isolated (any Actor)? = #isolation) async rethrows -> [T] where Element: Sendable { var values = [T]() for element in self { @@ -20,16 +20,18 @@ extension Sequence { return values } - func concurrentMap(_ transform: @escaping @Sendable (Element) async -> T) async -> [T] where Element: Sendable { - - let tasks = map { element in - Task { - await transform(element) + func concurrentMap(_ transform: @Sendable (Element) async -> T) async -> [T] where Element: Sendable { + return await withoutActuallyEscaping(transform) { escapingTransform in + + let tasks = map { element in + Task { + await escapingTransform(element) + } + } + + return await tasks.asyncMap { task in + await task.value } - } - - return await tasks.asyncMap { task in - await task.value } } diff --git a/Sources/YouTubeKit/Extensions/Retry.swift b/Sources/YouTubeKit/Extensions/Retry.swift index b2de76e..3988abe 100644 --- a/Sources/YouTubeKit/Extensions/Retry.swift +++ b/Sources/YouTubeKit/Extensions/Retry.swift @@ -17,7 +17,7 @@ extension Task { // A static function that attempts to execute a block of code with each method in a provided array. // If the block throws an error, the function will try the next method. // If all methods have been tried and all have thrown errors, the function will throw the last error encountered. - static func retry(with methods: [Method], block: (Method) async throws -> Success) async throws -> Success where Failure == Never { + static func retry(with methods: [Method], block: (Method) async throws -> Success, isolation: isolated (any Actor)? = #isolation) async throws -> Success where Failure == Never { var lastError: any Error = RetryError.emptyMethods diff --git a/Sources/YouTubeKit/YouTube.swift b/Sources/YouTubeKit/YouTube.swift index 6f65de6..2856eca 100644 --- a/Sources/YouTubeKit/YouTube.swift +++ b/Sources/YouTubeKit/YouTube.swift @@ -9,7 +9,7 @@ import Foundation @preconcurrency import os.log @available(iOS 13.0, watchOS 6.0, tvOS 13.0, macOS 10.15, *) -public class YouTube { +public actor YouTube { private var _js: String? private var _jsURL: URL? @@ -81,7 +81,7 @@ public class YouTube { } /// - parameter methods: Methods used to extract streams from the video - ordered by priority (Default: only local) - public convenience init(url: URL, proxies: [String: URL] = [:], useOAuth: Bool = false, allowOAuthCache: Bool = false, methods: [ExtractionMethod] = [.local]) { + public init(url: URL, proxies: [String: URL] = [:], useOAuth: Bool = false, allowOAuthCache: Bool = false, methods: [ExtractionMethod] = [.local]) { let videoID = Extraction.extractVideoID(from: url.absoluteString) ?? "" self.init(videoID: videoID, proxies: proxies, useOAuth: useOAuth, allowOAuthCache: allowOAuthCache, methods: methods) } @@ -220,7 +220,7 @@ public class YouTube { var streams = [Stream]() var existingITags = Set() - func process(streamingData: InnerTube.StreamingData, videoInfo: InnerTube.VideoInfo) async throws { + func process(streamingData: InnerTube.StreamingData, videoInfo: InnerTube.VideoInfo, isolation: isolated (any Actor)? = #isolation) async throws { var streamManifest = Extraction.applyDescrambler(streamData: streamingData)