Files
BiliDown/internal/router/task.go
T
yw1573 e8f71b04ac feat(task): add batch delete and cancel API support
支持批量删除和取消任务的接口:
- deleteTask 接口支持 POST 方式传入 ids 数组
- cancelTask 接口支持 POST 方式传入 ids 数组
- 前端批量操作改为调用批量接口,减少请求数量

Co-Authored-By: AI
2026-04-07 17:38:52 +08:00

276 lines
6.7 KiB
Go

package router
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"os"
"os/exec"
"runtime"
"strconv"
"bilidown/internal/task"
"bilidown/internal/util"
)
func createTask(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if r.Method != http.MethodPost {
util.Res{Success: false, Message: "不支持的请求方法"}.Write(w)
return
}
var body []task.TaskInDB
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
db := util.MustGetDB()
defer db.Close()
// 预先获取下载目录,避免循环中重复查询
folder, err := util.GetCurrentFolder(db)
if err != nil {
util.Res{Success: false, Message: fmt.Sprintf("获取下载目录失败: %v", err)}.Write(w)
return
}
for _, item := range body {
if !util.CheckBvidFormat(item.Bvid) {
util.Res{Success: false, Message: "bvid 格式错误"}.Write(w)
return
}
if item.Cover == "" || item.Title == "" || item.Owner == "" {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
if !util.IsValidURL(item.Cover) {
util.Res{Success: false, Message: "封面链接格式错误"}.Write(w)
return
}
if !util.IsValidURL(item.Audio) {
util.Res{Success: false, Message: "音频链接格式错误"}.Write(w)
return
}
if !util.IsValidURL(item.Video) {
util.Res{Success: false, Message: "视频链接格式错误"}.Write(w)
return
}
if !util.IsValidFormatCode(item.Format) {
util.Res{Success: false, Message: "清晰度代码错误"}.Write(w)
return
}
item.Folder = folder
item.Status = "waiting"
_task := task.Task{TaskInDB: item}
_task.Title = util.FilterFileName(_task.Title)
err = _task.Create(db)
if err != nil {
util.Res{Success: false, Message: fmt.Sprintf("_task.Create: %v.", err)}.Write(w)
return
}
go _task.Start()
}
util.Res{Success: true, Message: "创建成功"}.Write(w)
}
func getActiveTask(w http.ResponseWriter, r *http.Request) {
util.Res{Success: true, Data: task.GlobalTaskList}.Write(w)
}
func getTaskList(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
db := util.MustGetDB()
defer db.Close()
page, err := strconv.Atoi(r.FormValue("page"))
if err != nil {
page = 0
}
pageSize, err := strconv.Atoi(r.FormValue("pageSize"))
if err != nil {
pageSize = 360
}
tasks, err := task.GetTaskList(db, page, pageSize)
if err != nil {
util.Res{Success: false, Message: err.Error()}.Write(w)
return
}
util.Res{Success: true, Message: "获取成功", Data: tasks}.Write(w)
}
// showFile 调用 Explorer 查看文件位置
func showFile(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
filePath := r.FormValue("filePath")
var cmd *exec.Cmd
// 根据操作系统选择命令
switch runtime.GOOS {
case "windows":
// Windows 使用 explorer
cmd = exec.Command("explorer", "/select,", filePath)
case "darwin":
// macOS 使用 open
cmd = exec.Command("open", "-R", filePath)
case "linux":
// Linux 使用 xdg-open
cmd = exec.Command("xdg-open", filePath)
default:
util.Res{Success: false, Message: "不支持的操作系统"}.Write(w)
return
}
err := cmd.Start()
if err != nil {
util.Res{Success: false, Message: err.Error()}.Write(w)
return
}
util.Res{Success: true, Message: "操作成功"}.Write(w)
}
func deleteTask(w http.ResponseWriter, r *http.Request) {
// 支持单个删除(GET 参数 id)和批量删除(POST 参数 ids)
var taskIDs []int
if r.Method == http.MethodPost {
defer r.Body.Close()
var body struct {
IDs []int `json:"ids"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
taskIDs = body.IDs
} else {
taskIDStr := r.FormValue("id")
taskID, err := strconv.Atoi(taskIDStr)
if err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
taskIDs = []int{taskID}
}
if len(taskIDs) == 0 {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
db := util.MustGetDB()
defer db.Close()
// 批量删除结果
successCount := 0
failedTasks := []struct {
ID int
Error string
}{}
for _, taskID := range taskIDs {
_task, err := task.GetTask(db, taskID)
if err == sql.ErrNoRows {
// 数据库中没有该条记录,忽略
successCount++
continue
}
if err != nil {
failedTasks = append(failedTasks, struct {
ID int
Error string
}{ID: taskID, Error: fmt.Sprintf("获取任务失败: %v", err)})
continue
}
filePath := _task.FilePath()
err = os.Remove(filePath)
if err != nil && !os.IsNotExist(err) {
failedTasks = append(failedTasks, struct {
ID int
Error string
}{ID: taskID, Error: fmt.Sprintf("文件删除失败: %v", err)})
continue
}
err = task.DeleteTask(db, taskID)
if err != nil {
failedTasks = append(failedTasks, struct {
ID int
Error string
}{ID: taskID, Error: fmt.Sprintf("数据库删除失败: %v", err)})
continue
}
successCount++
}
if len(failedTasks) == 0 {
util.Res{Success: true, Message: fmt.Sprintf("成功删除 %d 个任务", successCount)}.Write(w)
} else {
util.Res{
Success: successCount > 0,
Message: fmt.Sprintf("成功删除 %d 个任务,失败 %d 个", successCount, len(failedTasks)),
Data: failedTasks,
}.Write(w)
}
}
func cancelTask(w http.ResponseWriter, r *http.Request) {
// 支持单个取消(GET 参数 id)和批量取消(POST 参数 ids)
var taskIDs []int64
if r.Method == http.MethodPost {
defer r.Body.Close()
var body struct {
IDs []int64 `json:"ids"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
taskIDs = body.IDs
} else {
taskIDStr := r.FormValue("id")
taskID, err := strconv.ParseInt(taskIDStr, 10, 64)
if err != nil {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
taskIDs = []int64{taskID}
}
if len(taskIDs) == 0 {
util.Res{Success: false, Message: "参数错误"}.Write(w)
return
}
// 批量取消结果
successCount := 0
failedIDs := []int64{}
for _, taskID := range taskIDs {
if task.CancelTask(taskID) {
successCount++
} else {
failedIDs = append(failedIDs, taskID)
}
}
if len(failedIDs) == 0 {
util.Res{Success: true, Message: fmt.Sprintf("成功取消 %d 个任务", successCount)}.Write(w)
} else {
util.Res{
Success: successCount > 0,
Message: fmt.Sprintf("成功取消 %d 个任务,失败 %d 个", successCount, len(failedIDs)),
Data: failedIDs,
}.Write(w)
}
}