regenerate something when the sentences changed
This commit is contained in:
@@ -33,11 +33,31 @@ final class SentenceExplanationStore {
|
||||
}
|
||||
}
|
||||
|
||||
func sync(mediaId: String, sentences: [SentenceBoundary]) {
|
||||
guard !sentences.isEmpty else { return }
|
||||
let directory = root.appendingPathComponent(Self.sanitize(mediaId), isDirectory: true)
|
||||
let revision = Self.revision(for: sentences)
|
||||
let marker = directory.appendingPathComponent("revision.txt")
|
||||
let previous = (try? String(contentsOf: marker, encoding: .utf8))?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard previous != revision else { return }
|
||||
try? FileManager.default.removeItem(at: directory)
|
||||
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||
try? revision.write(to: marker, atomically: true, encoding: .utf8)
|
||||
}
|
||||
|
||||
private func fileURL(mediaId: String, sentenceIndex: Int) -> URL {
|
||||
let directory = root.appendingPathComponent(Self.sanitize(mediaId), isDirectory: true)
|
||||
return directory.appendingPathComponent("\(sentenceIndex).txt")
|
||||
}
|
||||
|
||||
static func revision(for sentences: [SentenceBoundary]) -> String {
|
||||
sentences.map { sentence in
|
||||
let text = (sentence.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return "\(sentence.index)\t\(text)"
|
||||
}.joined(separator: "\n")
|
||||
}
|
||||
|
||||
static func sanitize(_ mediaId: String) -> String {
|
||||
let allowed = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "._-"))
|
||||
let filtered = mediaId.lowercased().unicodeScalars.map { allowed.contains($0) ? Character($0) : "_" }
|
||||
|
||||
@@ -45,6 +45,10 @@ final class CatalogViewModel: ObservableObject {
|
||||
do {
|
||||
let document = try await SentenceBoundaryAPI.fetch(videoHash: video.videoHash)
|
||||
boundaryDocuments[document.videoHash] = document
|
||||
SentenceExplanationStore.shared.sync(
|
||||
mediaId: document.videoHash,
|
||||
sentences: document.sentences
|
||||
)
|
||||
updatedVideos[index] = video.copy(
|
||||
durationMs: document.durationMs,
|
||||
sentenceCount: document.sentences.count
|
||||
@@ -75,6 +79,10 @@ final class CatalogViewModel: ObservableObject {
|
||||
return
|
||||
}
|
||||
if let document = boundaryDocuments[video.videoHash] {
|
||||
SentenceExplanationStore.shared.sync(
|
||||
mediaId: document.videoHash,
|
||||
sentences: document.sentences
|
||||
)
|
||||
let item = TrainingMediaItem(
|
||||
id: video.videoHash,
|
||||
title: video.title,
|
||||
@@ -98,6 +106,10 @@ final class CatalogViewModel: ObservableObject {
|
||||
return
|
||||
}
|
||||
boundaryDocuments[document.videoHash] = document
|
||||
SentenceExplanationStore.shared.sync(
|
||||
mediaId: document.videoHash,
|
||||
sentences: document.sentences
|
||||
)
|
||||
controller.updateBoundaries(document.sentences)
|
||||
controller.boundaryState = .loaded(count: document.sentences.count)
|
||||
controller.showTransientMessage("课程已就绪:\(document.sentences.count) 句")
|
||||
@@ -142,6 +154,10 @@ final class CatalogViewModel: ObservableObject {
|
||||
guard activeVideoHash == hash else {
|
||||
return
|
||||
}
|
||||
SentenceExplanationStore.shared.sync(
|
||||
mediaId: document.videoHash,
|
||||
sentences: document.sentences
|
||||
)
|
||||
controller.updateBoundaries(document.sentences)
|
||||
controller.boundaryState = .loaded(count: document.sentences.count)
|
||||
controller.showTransientMessage("句子边界已加载:\(document.sentences.count) 句")
|
||||
|
||||
@@ -29,7 +29,8 @@ struct FullscreenPlayerView: View {
|
||||
SentenceExplanationSheet(
|
||||
mediaId: controller.currentItem?.id ?? "",
|
||||
courseTitle: controller.currentItem?.title ?? "口语训练课程",
|
||||
sentence: sentence
|
||||
sentence: sentence,
|
||||
sentences: controller.currentItem?.sentences ?? []
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ struct PlayerSectionView: View {
|
||||
SentenceExplanationSheet(
|
||||
mediaId: controller.currentItem?.id ?? "",
|
||||
courseTitle: controller.currentItem?.title ?? "口语训练课程",
|
||||
sentence: sentence
|
||||
sentence: sentence,
|
||||
sentences: controller.currentItem?.sentences ?? []
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -273,6 +274,7 @@ struct SentenceExplanationSheet: View {
|
||||
let mediaId: String
|
||||
let courseTitle: String
|
||||
let sentence: SentenceBoundary
|
||||
var sentences: [SentenceBoundary] = []
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var explanation = ""
|
||||
@@ -327,6 +329,7 @@ struct SentenceExplanationSheet: View {
|
||||
}
|
||||
}
|
||||
.task {
|
||||
SentenceExplanationStore.shared.sync(mediaId: mediaId, sentences: sentences)
|
||||
if let cached = SentenceExplanationStore.shared.load(
|
||||
mediaId: mediaId,
|
||||
sentenceIndex: sentence.index
|
||||
|
||||
Reference in New Issue
Block a user