40 lines
2.1 KiB
Swift
40 lines
2.1 KiB
Swift
import Foundation
|
|
import Darwin
|
|
|
|
enum FFmpegRuntime {
|
|
static func error(for app: URL) -> String? {
|
|
let manifest = app.appendingPathComponent("Contents/Resources/ffmpeg-libraries.txt")
|
|
let metadataError = "This app's FFmpeg library metadata is missing or invalid. Rebuild and reinstall terebi."
|
|
guard let contents = try? String(contentsOf: manifest, encoding: .utf8) else {
|
|
return metadataError
|
|
}
|
|
var paths = contents.components(separatedBy: "\n")
|
|
if paths.last == "" { paths.removeLast() }
|
|
let libraries: Set<String> = ["libavformat", "libavcodec", "libavutil", "libswresample", "libswscale"]
|
|
guard paths.count == libraries.count else { return metadataError }
|
|
var found = Set<String>()
|
|
for path in paths {
|
|
guard path.hasPrefix("/"), !path.contains("\0"), !path.contains("\r") else {
|
|
return metadataError
|
|
}
|
|
let parts = URL(fileURLWithPath: path).lastPathComponent.split(separator: ".", omittingEmptySubsequences: false)
|
|
guard parts.count >= 2, parts.last == "dylib" else { return metadataError }
|
|
let library = String(parts[0])
|
|
guard libraries.contains(library),
|
|
parts.dropFirst().dropLast().allSatisfy({ !$0.isEmpty && $0.utf8.allSatisfy { (48...57).contains($0) } }),
|
|
found.insert(library).inserted else {
|
|
return metadataError
|
|
}
|
|
}
|
|
for path in paths {
|
|
_ = dlerror()
|
|
guard dlopen(path, RTLD_NOW | RTLD_LOCAL) != nil else {
|
|
let detail = dlerror().map { String(cString: $0) } ?? "The dynamic loader could not load this library."
|
|
return "The FFmpeg libraries required by this build are unavailable or broken.\n\nRequired library: \(path)\n\(detail)\n\nInstall FFmpeg with `brew install ffmpeg`. If FFmpeg was upgraded or its library versions changed, rebuild and reinstall terebi."
|
|
}
|
|
// Deliberately never dlclose: retain successful loads until exit.
|
|
}
|
|
return nil
|
|
}
|
|
}
|