feat: add task cancellation support
- Add CancelTask function to cancel running/waiting tasks - Add /api/cancelTask endpoint - Add cancel button in task list for running/waiting tasks - Check cancellation status during download and merge operations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ func API() *http.ServeMux {
|
||||
router.HandleFunc("/quit", quit)
|
||||
router.HandleFunc("/getPopularVideos", getPopularVideos)
|
||||
router.HandleFunc("/deleteTask", deleteTask)
|
||||
router.HandleFunc("/cancelTask", cancelTask)
|
||||
router.HandleFunc("/getRedirectedLocation", getRedirectedLocation)
|
||||
router.HandleFunc("/downloadVideo", downloadVideo)
|
||||
router.HandleFunc("/getSeasonsArchivesListFirstBvid", getSeasonsArchivesListFirstBvid)
|
||||
|
||||
@@ -170,3 +170,17 @@ func deleteTask(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
util.Res{Success: true, Message: "删除成功"}.Write(w)
|
||||
}
|
||||
|
||||
func cancelTask(w http.ResponseWriter, r *http.Request) {
|
||||
taskIDStr := r.FormValue("id")
|
||||
taskID, err := strconv.ParseInt(taskIDStr, 10, 64)
|
||||
if err != nil {
|
||||
util.Res{Success: false, Message: "参数错误"}.Write(w)
|
||||
return
|
||||
}
|
||||
if task.CancelTask(taskID) {
|
||||
util.Res{Success: true, Message: "任务已取消"}.Write(w)
|
||||
} else {
|
||||
util.Res{Success: false, Message: "任务不存在或已完成"}.Write(w)
|
||||
}
|
||||
}
|
||||
|
||||
+60
-9
@@ -59,7 +59,7 @@ func (task *TaskInDB) FilePath() string {
|
||||
)
|
||||
}
|
||||
|
||||
// done | waiting | running | error
|
||||
// done | waiting | running | error | cancelled
|
||||
type TaskStatus string
|
||||
|
||||
type Task struct {
|
||||
@@ -67,6 +67,7 @@ type Task struct {
|
||||
AudioProgress float64 `json:"audioProgress"`
|
||||
VideoProgress float64 `json:"videoProgress"`
|
||||
MergeProgress float64 `json:"mergeProgress"`
|
||||
Cancelled bool `json:"cancelled"`
|
||||
}
|
||||
|
||||
var GlobalTaskList = []*Task{}
|
||||
@@ -74,6 +75,20 @@ var GlobalTaskMux = &sync.Mutex{}
|
||||
var GlobalDownloadSem = util.NewSemaphore(3)
|
||||
var GlobalMergeSem = util.NewSemaphore(3)
|
||||
|
||||
// CancelTask 取消指定任务
|
||||
func CancelTask(taskID int64) bool {
|
||||
GlobalTaskMux.Lock()
|
||||
defer GlobalTaskMux.Unlock()
|
||||
for _, task := range GlobalTaskList {
|
||||
if task.ID == taskID && (task.Status == "waiting" || task.Status == "running") {
|
||||
task.Cancelled = true
|
||||
task.Status = "error"
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (task *Task) Create(db *sql.DB) error {
|
||||
util.SqliteLock.Lock()
|
||||
result, err := db.Exec(`INSERT INTO "task" ("bvid", "cid", "format", "title", "owner", "cover", "status", "folder", "duration", "download_type")
|
||||
@@ -109,6 +124,13 @@ func (task *Task) Start() {
|
||||
GlobalTaskMux.Unlock()
|
||||
db := util.MustGetDB()
|
||||
defer db.Close()
|
||||
|
||||
// 检查是否已取消
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
return
|
||||
}
|
||||
|
||||
sessdata, err := bilibili.GetSessdata(db)
|
||||
if err != nil {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("bilibili.GetSessdata: %v", err))
|
||||
@@ -124,14 +146,26 @@ func (task *Task) Start() {
|
||||
client := &bilibili.BiliClient{SESSDATA: sessdata}
|
||||
|
||||
GlobalDownloadSem.Acquire()
|
||||
|
||||
// 再次检查是否已取消
|
||||
if task.Cancelled {
|
||||
GlobalDownloadSem.Release()
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
return
|
||||
}
|
||||
|
||||
task.UpdateStatus(db, "running")
|
||||
|
||||
if task.DownloadType == "audio" {
|
||||
// 仅音频模式:只下载音频,重命名音频文件为输出文件
|
||||
err = DownloadMedia(client, task.Audio, task, "audio")
|
||||
if err != nil {
|
||||
if err != nil || task.Cancelled {
|
||||
GlobalDownloadSem.Release()
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
} else {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
GlobalDownloadSem.Release()
|
||||
@@ -151,9 +185,13 @@ func (task *Task) Start() {
|
||||
} else if task.DownloadType == "video" {
|
||||
// 仅视频模式:只下载视频,重命名视频文件为输出文件
|
||||
err = DownloadMedia(client, task.Video, task, "video")
|
||||
if err != nil {
|
||||
if err != nil || task.Cancelled {
|
||||
GlobalDownloadSem.Release()
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
} else {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
GlobalDownloadSem.Release()
|
||||
@@ -173,19 +211,32 @@ func (task *Task) Start() {
|
||||
} else {
|
||||
// 合并模式:下载音频和视频,然后合并
|
||||
err = DownloadMedia(client, task.Audio, task, "audio")
|
||||
if err != nil {
|
||||
if err != nil || task.Cancelled {
|
||||
GlobalDownloadSem.Release()
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
} else {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
err = DownloadMedia(client, task.Video, task, "video")
|
||||
if err != nil {
|
||||
if err != nil || task.Cancelled {
|
||||
GlobalDownloadSem.Release()
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
} else {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("DownloadMedia: %v", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
GlobalDownloadSem.Release()
|
||||
|
||||
if task.Cancelled {
|
||||
task.UpdateStatus(db, "error", fmt.Errorf("任务已取消"))
|
||||
return
|
||||
}
|
||||
|
||||
outputPath := task.TaskInDB.FilePath()
|
||||
videoPath := filepath.Join(task.Folder, strconv.FormatInt(task.ID, 10)+".video")
|
||||
audioPath := filepath.Join(task.Folder, strconv.FormatInt(task.ID, 10)+".audio")
|
||||
|
||||
Reference in New Issue
Block a user