111 lines
4.0 KiB
Swift
111 lines
4.0 KiB
Swift
import Foundation
|
||
|
||
struct RDEPUBLocationConverter {
|
||
|
||
// MARK: - 主路径:先构建目标章,拿到真实长度后再精确转换
|
||
|
||
/// 旧版 RDEPUBLocation -> 新版 RDEPUBChapterLocation
|
||
/// 主迁移路径:要求先构建目标章,用真实 chapterLength 做精确转换
|
||
/// 仅在无法获取章节长度时才降级到粗估 fallback
|
||
static func convert(
|
||
legacy location: RDEPUBLocation,
|
||
parser: RDEPUBParser,
|
||
publication: RDEPUBPublication,
|
||
chapterLengthProvider: ((Int) -> Int?)? = nil
|
||
) -> RDEPUBChapterLocation? {
|
||
// 1. 从 href 找到 spineIndex
|
||
guard let spineItem = publication.spine.first(where: {
|
||
$0.href == location.href || $0.href.contains(location.href)
|
||
}) else { return nil }
|
||
|
||
let spineIndex = publication.spine.firstIndex(of: spineItem) ?? 0
|
||
|
||
// 2. 优先用 fragmentID 定位(最精确,不受 progression 精度影响)
|
||
if let fragmentID = location.fragment {
|
||
return RDEPUBChapterLocation(
|
||
spineIndex: spineIndex,
|
||
chapterOffset: 0, // fragmentID 由 chapterOffsetMap 精确解析
|
||
fragmentID: fragmentID,
|
||
progressionInChapter: location.progression
|
||
)
|
||
}
|
||
|
||
// 3. 有 chapterLength 时做精确转换
|
||
if let provider = chapterLengthProvider,
|
||
let chapterLength = provider(spineIndex), chapterLength > 0 {
|
||
return convert(
|
||
legacy: location,
|
||
spineIndex: spineIndex,
|
||
chapterLength: chapterLength
|
||
)
|
||
}
|
||
|
||
// 4. Fallback:无法获取章节长度时的粗估(仅作临时降级)
|
||
let estimatedOffset = Int(location.progression * 10000)
|
||
return RDEPUBChapterLocation(
|
||
spineIndex: spineIndex,
|
||
chapterOffset: estimatedOffset,
|
||
fragmentID: nil,
|
||
progressionInChapter: location.progression,
|
||
schemaVersion: 1 // 标记为降级结果,后续可被精确值覆盖
|
||
)
|
||
}
|
||
|
||
/// 精确转换:已知章节实际长度
|
||
static func convert(
|
||
legacy location: RDEPUBLocation,
|
||
spineIndex: Int,
|
||
chapterLength: Int
|
||
) -> RDEPUBChapterLocation? {
|
||
let offset = Int(location.progression * Double(chapterLength))
|
||
return RDEPUBChapterLocation(
|
||
spineIndex: spineIndex,
|
||
chapterOffset: offset,
|
||
fragmentID: location.fragment,
|
||
progressionInChapter: location.progression,
|
||
schemaVersion: 2
|
||
)
|
||
}
|
||
|
||
/// 从已构建的 RDEPUBRuntimeChapter 做精确转换(推荐迁移路径)
|
||
static func convert(
|
||
legacy location: RDEPUBLocation,
|
||
chapter: RDEPUBRuntimeChapter
|
||
) -> RDEPUBChapterLocation? {
|
||
// 优先用 fragmentID
|
||
if let fragmentID = location.fragment,
|
||
let fragmentOffset = chapter.chapterOffsetMap.chapterOffset(forFragmentID: fragmentID) {
|
||
return RDEPUBChapterLocation(
|
||
spineIndex: chapter.spineIndex,
|
||
chapterOffset: fragmentOffset,
|
||
fragmentID: fragmentID,
|
||
progressionInChapter: nil,
|
||
schemaVersion: 2
|
||
)
|
||
}
|
||
|
||
// 用 progression + 真实长度
|
||
let chapterLength = chapter.typesetAttributedString.length
|
||
return convert(
|
||
legacy: location,
|
||
spineIndex: chapter.spineIndex,
|
||
chapterLength: chapterLength
|
||
)
|
||
}
|
||
|
||
/// 新版 -> 旧版(兼容外部接口)
|
||
static func toLegacy(
|
||
chapterLocation: RDEPUBChapterLocation,
|
||
href: String,
|
||
chapterLength: Int
|
||
) -> RDEPUBLocation {
|
||
let progression = chapterLength > 0
|
||
? Double(chapterLocation.chapterOffset) / Double(chapterLength)
|
||
: 0
|
||
return RDEPUBLocation(
|
||
href: href,
|
||
progression: min(max(progression, 0), 1),
|
||
fragment: chapterLocation.fragmentID
|
||
)
|
||
}
|
||
} |