mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-07-28 20:52:49 +00:00
136 lines
4.8 KiB
Swift
136 lines
4.8 KiB
Swift
import Foundation
|
|
|
|
@_silgen_name("fluxa_streaming_start_local_stream_server")
|
|
private func fluxaStreamingStartLocalStreamServer(
|
|
_ targetUrl: UnsafePointer<CChar>,
|
|
_ headersJson: UnsafePointer<CChar>,
|
|
_ preferredPort: Int32
|
|
) -> UnsafeMutablePointer<CChar>?
|
|
|
|
@_silgen_name("fluxa_streaming_stop_local_stream_server")
|
|
private func fluxaStreamingStopLocalStreamServer(_ serverId: UnsafePointer<CChar>) -> Bool
|
|
|
|
@_silgen_name("fluxa_streaming_start_torrent_server")
|
|
private func fluxaStreamingStartTorrentServer(
|
|
_ cacheDirectory: UnsafePointer<CChar>,
|
|
_ preferredPort: Int32,
|
|
_ accessToken: UnsafePointer<CChar>
|
|
) -> UnsafeMutablePointer<CChar>?
|
|
|
|
@_silgen_name("fluxa_streaming_stop_torrent_server")
|
|
private func fluxaStreamingStopTorrentServer() -> Bool
|
|
|
|
@_silgen_name("fluxa_streaming_string_free")
|
|
private func fluxaStreamingStringFree(_ value: UnsafeMutablePointer<CChar>)
|
|
|
|
final class FluxaAppleStreamingEngine: @unchecked Sendable {
|
|
static let shared = FluxaAppleStreamingEngine()
|
|
|
|
private let lock = NSLock()
|
|
private var localServerId: String?
|
|
private var torrentServerRunning = false
|
|
|
|
func prepare(url: String, requestHeadersJson: String, title: String) -> URL? {
|
|
lock.withLock {
|
|
stopLocked()
|
|
if isTorrent(url) {
|
|
return startTorrentLocked(link: url, title: title)
|
|
}
|
|
return startLocalProxyLocked(url: url, requestHeadersJson: requestHeadersJson)
|
|
}
|
|
}
|
|
|
|
func stop() {
|
|
lock.withLock {
|
|
stopLocked()
|
|
}
|
|
}
|
|
|
|
private func startLocalProxyLocked(url: String, requestHeadersJson: String) -> URL? {
|
|
guard let response = withCStrings(url, requestHeadersJson, operation: fluxaStreamingStartLocalStreamServer),
|
|
let server = try? JSONDecoder().decode(LocalServerResponse.self, from: Data(response.utf8)),
|
|
let proxyUrl = URL(string: server.url) else {
|
|
return nil
|
|
}
|
|
localServerId = server.id
|
|
return proxyUrl
|
|
}
|
|
|
|
private func startTorrentLocked(link: String, title: String) -> URL? {
|
|
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
|
|
.first?
|
|
.appendingPathComponent("fluxa_torrent_cache", isDirectory: true)
|
|
.path ?? ""
|
|
guard let response = withCStrings(cacheDirectory, "", operation: fluxaStreamingStartTorrentServer),
|
|
let server = try? JSONDecoder().decode(TorrentServerResponse.self, from: Data(response.utf8)),
|
|
var components = URLComponents(string: server.url) else {
|
|
return nil
|
|
}
|
|
components.path = components.path.appending("/stream/fname")
|
|
components.queryItems = [
|
|
URLQueryItem(name: "link", value: link),
|
|
URLQueryItem(name: "title", value: title)
|
|
]
|
|
torrentServerRunning = true
|
|
return components.url
|
|
}
|
|
|
|
private func stopLocked() {
|
|
if let localServerId {
|
|
localServerId.withCString { _ = fluxaStreamingStopLocalStreamServer($0) }
|
|
self.localServerId = nil
|
|
}
|
|
if torrentServerRunning {
|
|
_ = fluxaStreamingStopTorrentServer()
|
|
torrentServerRunning = false
|
|
}
|
|
}
|
|
|
|
private func isTorrent(_ url: String) -> Bool {
|
|
let normalized = url.lowercased()
|
|
return normalized.hasPrefix("magnet:") ||
|
|
normalized.hasPrefix("stremio://torrent/") ||
|
|
normalized.hasSuffix(".torrent")
|
|
}
|
|
|
|
private func withCStrings(
|
|
_ first: String,
|
|
_ second: String,
|
|
operation: (UnsafePointer<CChar>, UnsafePointer<CChar>, Int32) -> UnsafeMutablePointer<CChar>?
|
|
) -> String? {
|
|
first.withCString { firstPointer in
|
|
second.withCString { secondPointer in
|
|
guard let result = operation(firstPointer, secondPointer, 0) else {
|
|
return nil
|
|
}
|
|
defer { fluxaStreamingStringFree(result) }
|
|
return String(cString: result)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func withCStrings(
|
|
_ first: String,
|
|
_ second: String,
|
|
operation: (UnsafePointer<CChar>, Int32, UnsafePointer<CChar>) -> UnsafeMutablePointer<CChar>?
|
|
) -> String? {
|
|
first.withCString { firstPointer in
|
|
second.withCString { secondPointer in
|
|
guard let result = operation(firstPointer, 0, secondPointer) else {
|
|
return nil
|
|
}
|
|
defer { fluxaStreamingStringFree(result) }
|
|
return String(cString: result)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct LocalServerResponse: Decodable {
|
|
let id: String
|
|
let url: String
|
|
}
|
|
|
|
private struct TorrentServerResponse: Decodable {
|
|
let url: String
|
|
}
|
|
}
|