package jobs import ( "context" "errors" "path/filepath" "ddc/internal/extract" "ddc/internal/fetch" ) func (manager *Manager) Pause(id string) error { job, err := manager.lookup(id) if err != nil { return err } if !job.pause() { return ErrJobBusy } return nil } func (manager *Manager) Resume(id string) error { job, err := manager.lookup(id) if err != nil { return err } if !job.begin(StatusQueued, StatusPaused, StatusFailed) { return ErrJobBusy } job.saveRecord() manager.launchDownload(job) return nil } func (manager *Manager) Reorder(id, direction string) error { if direction != queueFirst && direction != queueUp && direction != queueDown { return invalid(errors.New("direction must be first, up or down")) } job, err := manager.lookup(id) if err != nil { return err } if job.currentStatus() != StatusQueued || !manager.slots.reorder(id, direction) { return ErrJobBusy } return nil } func (manager *Manager) launchDownload(job *Job) { runContext, cancelRun := job.startRun() manager.slots.enqueue(job.id) go func() { defer job.workers.Done() defer cancelRun(nil) manager.download(job, runContext) }() } func (manager *Manager) download(job *Job, runContext context.Context) { if !manager.slots.acquire(runContext, job.id, manager.concurrencyLimit) { manager.handleDownloadError(job, runContext, context.Cause(runContext)) return } defer manager.slots.release() job.setStatus(StatusDownloading, "") filePath, err := manager.downloader.Download(runContext, manager.fetchRequest(job)) if err != nil { manager.handleDownloadError(job, runContext, err) return } job.markDownloaded(filepath.Base(filePath)) if job.autoExtract && extract.IsArchive(filePath) { job.setStatus(StatusExtracting, "") destination, err := manager.extractArchive(job, filePath) if err != nil { manager.finishWithError(job, StatusReady, "Extraction failed: ", err) job.saveRecord() return } job.SetFileName(filepath.Base(destination)) } job.setStatus(StatusReady, "") job.saveRecord() manager.moveToTarget(job) } func (manager *Manager) fetchRequest(job *Job) fetch.Request { current := manager.preferences.Get() return fetch.Request{ URL: job.sourceURL, Directory: job.directory, FileName: job.requestedName, ReservedBytes: current.MinFreeBytes(), Connections: current.ConnectionsPerDownload, State: job.currentDownloadState(), Progress: job, Checkpoint: func(state fetch.State) { job.setDownloadState(&state) job.saveRecord() }, } } func (manager *Manager) handleDownloadError(job *Job, runContext context.Context, err error) { switch { case errors.Is(context.Cause(runContext), errPaused): job.setStatus(StatusPaused, "") case job.jobContext.Err() != nil: job.setStatus(StatusCancelled, "") return default: if errors.Is(err, fetch.ErrSourceChanged) || errors.Is(err, fetch.ErrResumeUnsupported) { job.setDownloadState(nil) } job.setStatus(StatusFailed, err.Error()) } job.saveRecord() }