71 lines
3.2 KiB
Swift
71 lines
3.2 KiB
Swift
import Foundation
|
|
|
|
/// Resolve a materialId + materialType to a local file path for the reader.
|
|
struct MaterialPathResolver {
|
|
/// Try to locate the file on disk given the continue-learning API response.
|
|
/// Returns (filePath, materialType) or nil if the file cannot be found.
|
|
static func resolve(materialId: String, apiType: String?, title: String?) -> (filePath: String, materialType: MaterialType)? {
|
|
let mt = materialTypeFrom(apiType: apiType, title: title)
|
|
|
|
// 1. knowledge_source: check actual download paths first (tmp/), then legacy paths
|
|
if apiType == "knowledge_source" {
|
|
// Priority 1: tmp/ — where MaterialReaderView and KnowledgeDetailVM actually download to
|
|
let tmpDir = FileManager.default.temporaryDirectory
|
|
if let files = try? FileManager.default.contentsOfDirectory(at: tmpDir, includingPropertiesForKeys: nil) {
|
|
for f in files {
|
|
let name = f.lastPathComponent
|
|
if name.hasPrefix("source_\(materialId).") || name.hasPrefix("kd_\(materialId).") {
|
|
return (f.path, mt)
|
|
}
|
|
}
|
|
}
|
|
// Priority 2: legacy path Documents/knowledge_sources/<id>/
|
|
if let path = resolveKnowledgeSource(id: materialId) {
|
|
return (path, mt)
|
|
}
|
|
}
|
|
|
|
// 2. Temporary file: check Documents directory
|
|
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
let tmpPath = docs.appendingPathComponent(materialId).path
|
|
if FileManager.default.fileExists(atPath: tmpPath) {
|
|
return (tmpPath, mt)
|
|
}
|
|
|
|
// 3. Check cache directory
|
|
let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
|
|
let cachePath = cache.appendingPathComponent(materialId).path
|
|
if FileManager.default.fileExists(atPath: cachePath) {
|
|
return (cachePath, mt)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private static func resolveKnowledgeSource(id: String) -> String? {
|
|
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
// Knowledge sources are stored as <Documents>/knowledge_sources/<id>/<filename>
|
|
let dir = docs.appendingPathComponent("knowledge_sources/\(id)")
|
|
guard let files = try? FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil),
|
|
let first = files.first else { return nil }
|
|
return first.path
|
|
}
|
|
|
|
private static func materialTypeFrom(apiType: String?, title: String?) -> MaterialType {
|
|
switch apiType {
|
|
case "knowledge_source", "temporary_file":
|
|
// Infer from title extension
|
|
if let t = title?.lowercased() {
|
|
if t.hasSuffix(".md") { return .markdown }
|
|
if t.hasSuffix(".txt") { return .text }
|
|
if t.hasSuffix(".pdf") { return .pdf }
|
|
if t.hasSuffix(".epub") { return .epub }
|
|
if t.hasSuffix(".png") || t.hasSuffix(".jpg") || t.hasSuffix(".jpeg") || t.hasSuffix(".webp") { return .image }
|
|
}
|
|
return .unknown
|
|
default:
|
|
return .unknown
|
|
}
|
|
}
|
|
}
|