package jobs import ( "io/fs" "os" "path/filepath" "regexp" "sort" "time" "ddc/internal/files" ) var jobIDPattern = regexp.MustCompile(`^[0-9a-f]{32}$`) func (manager *Manager) RestoreExisting() error { entries, err := os.ReadDir(manager.settings.DownloadDir) if err != nil { return err } var queuedJobs []*Job for _, entry := range entries { if !entry.IsDir() || !jobIDPattern.MatchString(entry.Name()) { continue } job := manager.restoreJob(entry) manager.store(job) if job.status == StatusQueued { queuedJobs = append(queuedJobs, job) } } sort.Slice(queuedJobs, func(first, second int) bool { return queuedJobs[first].createdAt.Before(queuedJobs[second].createdAt) }) for _, job := range queuedJobs { job.workers.Add(1) manager.launchDownload(job) } return nil } func (manager *Manager) restoreJob(entry fs.DirEntry) *Job { directory := filepath.Join(manager.settings.DownloadDir, entry.Name()) createdAt := time.Now() if info, err := entry.Info(); err == nil { createdAt = info.ModTime() } job := manager.newJob(entry.Name(), directory, createdAt) record, found := readRecord(directory) if found { job.applyRecord(record) } if !found || record.Phase == phaseDone { os.Remove(filepath.Join(directory, files.PartFileName)) job.status = StatusReady job.downloaded = true job.fileName = firstEntryName(directory, entry.Name()) return job } job.downloadState = record.Download if record.Download != nil { job.bytesDone.Store(record.Download.DoneBytes()) job.bytesTotal.Store(record.Download.TotalBytes) } switch record.Phase { case phasePaused: job.status = StatusPaused case phaseFailed: job.status = StatusFailed job.message = record.Message default: job.status = StatusQueued } return job } func firstEntryName(directory, fallback string) string { entries, err := os.ReadDir(directory) if err != nil { return fallback } for _, entry := range entries { if !files.IsInternal(entry.Name()) { return entry.Name() } } return fallback }