Add project save and load controls
This commit is contained in:
@@ -39,6 +39,11 @@ struct ContentView: View {
|
|||||||
) {
|
) {
|
||||||
selectOutputFile()
|
selectOutputFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button("Projekt laden") {
|
||||||
|
selectProjectFile()
|
||||||
|
}
|
||||||
|
.help("Lädt eine gespeicherte .growthlapse-project.json, inklusive Segmentfenstern und Variantenwahl.")
|
||||||
}
|
}
|
||||||
.disabled(processor.state.isRunning)
|
.disabled(processor.state.isRunning)
|
||||||
|
|
||||||
@@ -365,6 +370,12 @@ struct ContentView: View {
|
|||||||
.disabled(processor.state.isRunning || !project.clips.contains(where: \.needsRender))
|
.disabled(processor.state.isRunning || !project.clips.contains(where: \.needsRender))
|
||||||
.help("Rendert nur geänderte normalisierte Clips neu und baut danach das finale Video erneut.")
|
.help("Rendert nur geänderte normalisierte Clips neu und baut danach das finale Video erneut.")
|
||||||
|
|
||||||
|
Button("Projekt speichern") {
|
||||||
|
processor.saveCurrentProject()
|
||||||
|
}
|
||||||
|
.disabled(processor.state.isRunning)
|
||||||
|
.help("Speichert die aktuelle Nachbearbeitung in die .growthlapse-project.json neben der Output-Datei. Segmentänderungen werden zusätzlich automatisch gespeichert.")
|
||||||
|
|
||||||
Button("Ergebnis bestätigen") {
|
Button("Ergebnis bestätigen") {
|
||||||
processor.confirmProjectAndDeleteCache()
|
processor.confirmProjectAndDeleteCache()
|
||||||
}
|
}
|
||||||
@@ -619,6 +630,23 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func selectProjectFile() {
|
||||||
|
let panel = NSOpenPanel()
|
||||||
|
panel.title = "GrowthLapse-Projekt laden"
|
||||||
|
panel.canChooseFiles = true
|
||||||
|
panel.canChooseDirectories = false
|
||||||
|
panel.allowsMultipleSelection = false
|
||||||
|
panel.allowedContentTypes = [.json]
|
||||||
|
panel.directoryURL = settings.outputFile?.deletingLastPathComponent()
|
||||||
|
if panel.runModal() == .OK,
|
||||||
|
let url = panel.url,
|
||||||
|
let project = processor.loadProject(from: url) {
|
||||||
|
settings.outputFile = project.outputFileURL
|
||||||
|
settings.inputFolder = project.clips.first?.sourceURL.deletingLastPathComponent()
|
||||||
|
selectedClipID = project.clips.first?.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func selectExecutable(title: String, onSelect: (URL) -> Void) {
|
private func selectExecutable(title: String, onSelect: (URL) -> Void) {
|
||||||
let panel = NSOpenPanel()
|
let panel = NSOpenPanel()
|
||||||
panel.title = title
|
panel.title = title
|
||||||
|
|||||||
@@ -137,6 +137,42 @@ final class VideoProcessor: ObservableObject {
|
|||||||
saveProject(reordered)
|
saveProject(reordered)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveCurrentProject() {
|
||||||
|
guard let project else {
|
||||||
|
appendLog("Kein Projekt zum Speichern geladen.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saveProject(project)
|
||||||
|
appendLog("Projekt gespeichert: \(GrowthLapseProject.url(for: project.outputFileURL).path)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadProject(from url: URL) -> GrowthLapseProject? {
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: url)
|
||||||
|
var loadedProject = try JSONDecoder().decode(GrowthLapseProject.self, from: data)
|
||||||
|
loadedProject.clips = loadedProject.clips.map { clip in
|
||||||
|
var updated = clip
|
||||||
|
if !FileManager.default.fileExists(atPath: updated.normalizedClipPath) {
|
||||||
|
updated.needsRender = true
|
||||||
|
}
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
project = loadedProject
|
||||||
|
state = .finished(loadedProject.outputFileURL)
|
||||||
|
progress = 1
|
||||||
|
progressText = "Projekt geladen"
|
||||||
|
appendLog("Projekt geladen: \(url.path)")
|
||||||
|
if loadedProject.clips.contains(where: \.needsRender) {
|
||||||
|
appendLog("Hinweis: Einige Cache-Clips fehlen und werden beim nächsten Render neu erzeugt.")
|
||||||
|
}
|
||||||
|
return loadedProject
|
||||||
|
} catch {
|
||||||
|
state = .failed("Projekt konnte nicht geladen werden: \(error.localizedDescription)")
|
||||||
|
appendLog("Fehler: Projekt konnte nicht geladen werden: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func confirmProjectAndDeleteCache() {
|
func confirmProjectAndDeleteCache() {
|
||||||
guard let project else {
|
guard let project else {
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user