mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-30 19:46:38 +08:00
@@ -100,13 +100,11 @@ func (h *DownloadClientHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 热插拔:加载新客户端
|
||||
go func() {
|
||||
if initErr := h.svc.DownloadMgr.AddClient(ctx, client); initErr != nil {
|
||||
h.log.Warn("failed to hot-add download client", zap.Error(initErr))
|
||||
}
|
||||
}()
|
||||
_ = h.svc.Downloads.ReloadConfig(ctx)
|
||||
if err := h.svc.Downloads.ReloadConfig(ctx); err != nil {
|
||||
h.log.Warn("failed to reload download clients after create", zap.Error(err))
|
||||
Error(c, http.StatusInternalServerError, ErrInternal, "客户端已保存,但运行时重载失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Success(c, client)
|
||||
}
|
||||
@@ -207,13 +205,11 @@ func (h *DownloadClientHandler) Update(c *gin.Context) {
|
||||
}
|
||||
clearLegacyQBitSettingsIfNoDefault(c.Request.Context(), h.svc)
|
||||
|
||||
// 热更新适配器
|
||||
go func() {
|
||||
if updateErr := h.svc.DownloadMgr.UpdateClient(ctx, client); updateErr != nil {
|
||||
h.log.Warn("failed to hot-update download client", zap.Error(updateErr))
|
||||
}
|
||||
}()
|
||||
_ = h.svc.Downloads.ReloadConfig(ctx)
|
||||
if err := h.svc.Downloads.ReloadConfig(ctx); err != nil {
|
||||
h.log.Warn("failed to reload download clients after update", zap.Error(err))
|
||||
Error(c, http.StatusInternalServerError, ErrInternal, "客户端已更新,但运行时重载失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Success(c, client)
|
||||
}
|
||||
@@ -235,10 +231,12 @@ func (h *DownloadClientHandler) Delete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 热移除
|
||||
h.svc.DownloadMgr.RemoveClient(id)
|
||||
clearLegacyQBitSettingsIfNoDefault(c.Request.Context(), h.svc)
|
||||
_ = h.svc.Downloads.ReloadConfig(ctx)
|
||||
if err := h.svc.Downloads.ReloadConfig(ctx); err != nil {
|
||||
h.log.Warn("failed to reload download clients after delete", zap.Error(err))
|
||||
Error(c, http.StatusInternalServerError, ErrInternal, "客户端已删除,但运行时重载失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessWithMessage(c, "已删除", nil)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,10 @@ func createDownloadClientHandler(svc *service.Container) gin.HandlerFunc {
|
||||
}
|
||||
// 让真正发起下载的 DownloadService 立刻读到新的 qb 配置,
|
||||
// 避免保存后还要重启进程才能生效。
|
||||
_ = svc.Downloads.ReloadConfig(c.Request.Context())
|
||||
if err := svc.Downloads.ReloadConfig(c.Request.Context()); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "client saved but runtime reload failed: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, row)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +59,10 @@ func updateDownloadClientHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = svc.Downloads.ReloadConfig(c.Request.Context())
|
||||
if err := svc.Downloads.ReloadConfig(c.Request.Context()); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "client updated but runtime reload failed: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, row)
|
||||
}
|
||||
}
|
||||
@@ -67,7 +73,10 @@ func deleteDownloadClientHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = svc.Downloads.ReloadConfig(c.Request.Context())
|
||||
if err := svc.Downloads.ReloadConfig(c.Request.Context()); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "client deleted but runtime reload failed: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,11 +138,26 @@ func visibleLiveTorrents(rows []model.DownloadTask, live []service.QBitTorrent)
|
||||
}
|
||||
filtered := make([]service.QBitTorrent, 0, len(live))
|
||||
for _, torrent := range live {
|
||||
matchedByID := false
|
||||
for _, row := range rows {
|
||||
if strings.TrimSpace(row.ExternalID) != "" && strings.EqualFold(row.ExternalID, torrent.Hash) &&
|
||||
(strings.TrimSpace(row.DownloadClientID) == "" || row.DownloadClientID == torrent.ClientID) {
|
||||
filtered = append(filtered, torrent)
|
||||
matchedByID = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if matchedByID {
|
||||
continue
|
||||
}
|
||||
torrentTitle := normalizeTitle(torrent.Name)
|
||||
if torrentTitle == "" {
|
||||
continue
|
||||
}
|
||||
for _, row := range rows {
|
||||
if strings.TrimSpace(row.DownloadClientID) != "" && strings.TrimSpace(torrent.ClientID) != "" && row.DownloadClientID != torrent.ClientID {
|
||||
continue
|
||||
}
|
||||
rowTitle := normalizeTitle(row.Title)
|
||||
if rowTitle == "" {
|
||||
continue
|
||||
@@ -197,7 +212,7 @@ func deleteDownloadHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
hash := c.Param("hash")
|
||||
withFiles := c.Query("delete_files") == "true"
|
||||
if err := svc.Downloads.Delete(c.Request.Context(), hash, withFiles); err != nil {
|
||||
if err := svc.Downloads.Delete(c.Request.Context(), hash, withFiles, c.Query("client_id")); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -208,6 +223,7 @@ func deleteDownloadHandler(svc *service.Container) gin.HandlerFunc {
|
||||
type relocateDownloadReq struct {
|
||||
Hash string `json:"hash" binding:"required"`
|
||||
Location string `json:"location" binding:"required"`
|
||||
ClientID string `json:"client_id"`
|
||||
}
|
||||
|
||||
// relocateDownloadHandler moves a torrent's data to a new directory while
|
||||
@@ -219,8 +235,12 @@ func relocateDownloadHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := svc.Downloads.RelocateTorrent(c.Request.Context(), req.Hash, req.Location); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
if err := svc.Downloads.RelocateTorrent(c.Request.Context(), req.Hash, req.Location, req.ClientID); err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
if errors.Is(err, service.ErrDownloadOperationUnsupported) {
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
c.JSON(status, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"hash": strings.TrimSpace(req.Hash), "location": strings.TrimSpace(req.Location)})
|
||||
|
||||
@@ -11,15 +11,9 @@ import (
|
||||
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||||
)
|
||||
|
||||
// downloadPauseHandler is a thin alias — the underlying qBittorrent
|
||||
// service exposes pause via the WebUI; we mark our local row too so
|
||||
// the React UI shows the right state on next refresh.
|
||||
func downloadPauseHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := svc.Repo.DB.WithContext(c.Request.Context()).
|
||||
Model(&model.DownloadTask{}).
|
||||
Where("id = ?", c.Param("id")).
|
||||
Update("status", "paused").Error; err != nil {
|
||||
if err := svc.Downloads.PauseDownloadTask(c.Request.Context(), c.Param("id")); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -27,13 +21,9 @@ func downloadPauseHandler(svc *service.Container) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// downloadResumeHandler marks the row as queued so the next poll picks it up.
|
||||
func downloadResumeHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := svc.Repo.DB.WithContext(c.Request.Context()).
|
||||
Model(&model.DownloadTask{}).
|
||||
Where("id = ?", c.Param("id")).
|
||||
Update("status", "queued").Error; err != nil {
|
||||
if err := svc.Downloads.ResumeDownloadTask(c.Request.Context(), c.Param("id")); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user