Store unfinished tasks on disks
This commit is contained in:
@@ -2,6 +2,7 @@ package copier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
@@ -73,6 +74,15 @@ func (c *Copier) LastCopiedAt(diskID string) (time.Time, bool, error) {
|
||||
}
|
||||
|
||||
func (c *Copier) Start(ctx context.Context, opts Options) (string, error) {
|
||||
return c.startTask(ctx, "", opts)
|
||||
}
|
||||
|
||||
func (c *Copier) Resume(ctx context.Context, taskID string, opts Options) error {
|
||||
_, err := c.startTask(ctx, taskID, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Copier) startTask(ctx context.Context, existingTaskID string, opts Options) (string, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -86,8 +96,13 @@ func (c *Copier) Start(ctx context.Context, opts Options) (string, error) {
|
||||
}
|
||||
|
||||
if opts.DestFolder == "" {
|
||||
opts.DestFolder = "media"
|
||||
opts.DestFolder = config.DefaultDestFolder
|
||||
}
|
||||
destFolder, err := config.NormalizeDestFolder(opts.DestFolder)
|
||||
if err != nil {
|
||||
destFolder = config.DefaultDestFolder
|
||||
}
|
||||
opts.DestFolder = destFolder
|
||||
|
||||
_, free, err := disk.DiskUsage(opts.MountPath)
|
||||
if err != nil {
|
||||
@@ -98,12 +113,39 @@ func (c *Copier) Start(ctx context.Context, opts Options) (string, error) {
|
||||
return "", errors.New("free space is below reserve threshold")
|
||||
}
|
||||
|
||||
t := c.tasks.Create("copy", opts.DiskID)
|
||||
var taskID string
|
||||
if existingTaskID == "" {
|
||||
t := c.tasks.Create("copy", opts.DiskID)
|
||||
payload, err := json.Marshal(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := database.UpsertTask(*t, payload); err != nil {
|
||||
return "", err
|
||||
}
|
||||
taskID = t.ID
|
||||
} else {
|
||||
taskID = existingTaskID
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusQueued
|
||||
t.Phase = task.PhaseQueued
|
||||
t.Message = "Resuming after restart..."
|
||||
t.Error = ""
|
||||
t.SpeedBPS = 0
|
||||
t.ETASec = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
if err := database.UpdateTask(*t); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copyCtx, cancel := context.WithCancel(ctx)
|
||||
c.cancels[opts.DiskID] = cancel
|
||||
|
||||
go c.run(copyCtx, t.ID, opts, database)
|
||||
return t.ID, nil
|
||||
go c.run(copyCtx, taskID, opts, database)
|
||||
return taskID, nil
|
||||
}
|
||||
|
||||
func (c *Copier) Cancel(diskID string) {
|
||||
@@ -127,20 +169,43 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
t.Message = msg
|
||||
t.Progress = prog
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
}
|
||||
fail := func(err error) {
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusFailed
|
||||
t.Error = err.Error()
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
}
|
||||
|
||||
setStatus(task.StatusRunning, "Preparing...", 0)
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusRunning
|
||||
t.Phase = task.PhasePreparing
|
||||
t.Message = "Preparing..."
|
||||
t.Progress = 0
|
||||
t.Error = ""
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
|
||||
destRoot := filepath.Join(opts.MountPath, opts.DestFolder)
|
||||
|
||||
if opts.OverwriteMode == config.OverwriteDelete {
|
||||
setStatus(task.StatusRunning, "Replacing destination media...", 0)
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusRunning
|
||||
t.Phase = task.PhaseReplacing
|
||||
t.Message = "Replacing destination media..."
|
||||
t.Progress = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
if err := os.RemoveAll(destRoot); err != nil {
|
||||
fail(err)
|
||||
return
|
||||
@@ -149,7 +214,15 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
|
||||
var copiedPaths map[string]struct{}
|
||||
if opts.FileSelectMode == config.SelectNew {
|
||||
setStatus(task.StatusRunning, "Loading copy history...", 0)
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusRunning
|
||||
t.Phase = task.PhaseLoadingHistory
|
||||
t.Message = "Loading copy history..."
|
||||
t.Progress = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
var err error
|
||||
copiedPaths, err = database.CopiedPaths(opts.DiskID)
|
||||
if err != nil {
|
||||
@@ -158,7 +231,15 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
}
|
||||
}
|
||||
|
||||
setStatus(task.StatusRunning, "Scanning sources...", 0)
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusRunning
|
||||
t.Phase = task.PhaseScanning
|
||||
t.Message = "Scanning sources..."
|
||||
t.Progress = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
files, err := buildFileList(opts.MediaPath, opts.SourceRules, copiedPaths)
|
||||
if err != nil {
|
||||
fail(err)
|
||||
@@ -204,6 +285,9 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
t.SpeedBPS = 0
|
||||
t.ETASec = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
@@ -227,11 +311,15 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
|
||||
c.tasks.Update(taskID, func(t *task.Task) {
|
||||
t.Status = task.StatusRunning
|
||||
t.Phase = task.PhaseCopying
|
||||
t.Message = msg
|
||||
t.Progress = prog
|
||||
t.SpeedBPS = speedBPS
|
||||
t.ETASec = int(etaSec)
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
|
||||
dstAbs := filepath.Join(destRoot, f.relPath)
|
||||
if err := rsyncFile(ctx, f.srcAbs, dstAbs); err != nil {
|
||||
@@ -242,6 +330,9 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
t.SpeedBPS = 0
|
||||
t.ETASec = 0
|
||||
})
|
||||
if t, ok := c.tasks.Get(taskID); ok {
|
||||
_ = database.UpdateTask(*t)
|
||||
}
|
||||
return
|
||||
}
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user