Phase 3: Add history data structures and persistence
Added conversion history tracking with persistence to disk. Jobs are
automatically added to history when they complete, fail, or are cancelled.
Changes:
- Added HistoryEntry struct to represent completed jobs
- Added historyConfig for JSON persistence
- Added historyConfigPath(), loadHistoryConfig(), saveHistoryConfig() functions
- Added historyEntries and sidebarVisible fields to appState
- Added addToHistory() method to save completed jobs
- Initialize history loading on app startup
- Hook into queue change callback to automatically save finished jobs
- Store FFmpeg command in history for each job
- Limit history to 20 most recent entries
History is saved to ~/.config/VideoTools/history.json and includes job
details, timestamps, error messages, and the FFmpeg command for manual
reproduction.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
bccacf9ea2
commit
d785e4dc91
140
main.go
140
main.go
|
|
@ -593,6 +593,74 @@ func saveBenchmarkConfig(cfg benchmarkConfig) error {
|
|||
return os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
|
||||
// HistoryEntry represents a completed job in the history
|
||||
type HistoryEntry struct {
|
||||
ID string `json:"id"`
|
||||
Type queue.JobType `json:"type"`
|
||||
Status queue.JobStatus `json:"status"`
|
||||
Title string `json:"title"`
|
||||
InputFile string `json:"input_file"`
|
||||
OutputFile string `json:"output_file"`
|
||||
LogPath string `json:"log_path,omitempty"`
|
||||
Config map[string]interface{} `json:"config"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
FFmpegCmd string `json:"ffmpeg_cmd,omitempty"`
|
||||
}
|
||||
|
||||
// historyConfig holds conversion history
|
||||
type historyConfig struct {
|
||||
Entries []HistoryEntry `json:"entries"`
|
||||
}
|
||||
|
||||
func historyConfigPath() string {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil || configDir == "" {
|
||||
home := os.Getenv("HOME")
|
||||
if home != "" {
|
||||
configDir = filepath.Join(home, ".config")
|
||||
}
|
||||
}
|
||||
if configDir == "" {
|
||||
return "history.json"
|
||||
}
|
||||
return filepath.Join(configDir, "VideoTools", "history.json")
|
||||
}
|
||||
|
||||
func loadHistoryConfig() (historyConfig, error) {
|
||||
path := historyConfigPath()
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return historyConfig{Entries: []HistoryEntry{}}, nil
|
||||
}
|
||||
return historyConfig{}, err
|
||||
}
|
||||
var cfg historyConfig
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
return historyConfig{}, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func saveHistoryConfig(cfg historyConfig) error {
|
||||
// Limit to 20 most recent entries
|
||||
if len(cfg.Entries) > 20 {
|
||||
cfg.Entries = cfg.Entries[:20]
|
||||
}
|
||||
path := historyConfigPath()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
|
||||
type appState struct {
|
||||
window fyne.Window
|
||||
active string
|
||||
|
|
@ -697,6 +765,10 @@ type appState struct {
|
|||
// Interlacing detection state
|
||||
interlaceResult *interlace.DetectionResult
|
||||
interlaceAnalyzing bool
|
||||
|
||||
// History sidebar state
|
||||
historyEntries []HistoryEntry
|
||||
sidebarVisible bool
|
||||
}
|
||||
|
||||
type mergeClip struct {
|
||||
|
|
@ -711,6 +783,55 @@ func (s *appState) persistConvertConfig() {
|
|||
}
|
||||
}
|
||||
|
||||
// addToHistory adds a completed job to the history
|
||||
func (s *appState) addToHistory(job *queue.Job) {
|
||||
if job == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Only add completed, failed, or cancelled jobs
|
||||
if job.Status != queue.JobStatusCompleted &&
|
||||
job.Status != queue.JobStatusFailed &&
|
||||
job.Status != queue.JobStatusCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
// Build FFmpeg command from job config
|
||||
cmdStr := buildFFmpegCommandFromJob(job)
|
||||
|
||||
entry := HistoryEntry{
|
||||
ID: job.ID,
|
||||
Type: job.Type,
|
||||
Status: job.Status,
|
||||
Title: job.Title,
|
||||
InputFile: job.InputFile,
|
||||
OutputFile: job.OutputFile,
|
||||
LogPath: job.LogPath,
|
||||
Config: job.Config,
|
||||
CreatedAt: job.CreatedAt,
|
||||
StartedAt: job.StartedAt,
|
||||
CompletedAt: job.CompletedAt,
|
||||
Error: job.Error,
|
||||
FFmpegCmd: cmdStr,
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
for _, existing := range s.historyEntries {
|
||||
if existing.ID == entry.ID {
|
||||
return // Already in history
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend to history (newest first)
|
||||
s.historyEntries = append([]HistoryEntry{entry}, s.historyEntries...)
|
||||
|
||||
// Save to disk
|
||||
cfg := historyConfig{Entries: s.historyEntries}
|
||||
if err := saveHistoryConfig(cfg); err != nil {
|
||||
logging.Debug(logging.CatSystem, "failed to save history: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *appState) stopPreview() {
|
||||
if s.anim != nil {
|
||||
s.anim.Stop()
|
||||
|
|
@ -4591,6 +4712,15 @@ func runGUI() {
|
|||
logging.Debug(logging.CatSystem, "failed to load persisted convert config: %v", err)
|
||||
}
|
||||
|
||||
// Initialize conversion history
|
||||
if historyCfg, err := loadHistoryConfig(); err == nil {
|
||||
state.historyEntries = historyCfg.Entries
|
||||
} else {
|
||||
state.historyEntries = []HistoryEntry{}
|
||||
logging.Debug(logging.CatSystem, "failed to load history config: %v", err)
|
||||
}
|
||||
state.sidebarVisible = false
|
||||
|
||||
// Initialize conversion stats bar
|
||||
state.statsBar = ui.NewConversionStatsBar(func() {
|
||||
// Clicking the stats bar opens the queue view
|
||||
|
|
@ -4605,6 +4735,16 @@ func runGUI() {
|
|||
return
|
||||
}
|
||||
app.Driver().DoFromGoroutine(func() {
|
||||
// Add completed jobs to history
|
||||
jobs := state.jobQueue.List()
|
||||
for _, job := range jobs {
|
||||
if job.Status == queue.JobStatusCompleted ||
|
||||
job.Status == queue.JobStatusFailed ||
|
||||
job.Status == queue.JobStatusCancelled {
|
||||
state.addToHistory(job)
|
||||
}
|
||||
}
|
||||
|
||||
state.updateStatsBar()
|
||||
state.updateQueueButtonLabel()
|
||||
if state.active == "queue" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user