ios-projects/AIStudyApp/AIStudyApp/Core/Services/MaterialPathResolver.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
}
}
}