mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
102 lines
3.2 KiB
Go
102 lines
3.2 KiB
Go
// Package handler — download manager endpoints.
|
||
package handler
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/ShukeBta/MediaStationGo/internal/middleware"
|
||
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||
)
|
||
|
||
type addDownloadReq struct {
|
||
URL string `json:"url" binding:"required"`
|
||
SavePath string `json:"save_path"`
|
||
}
|
||
|
||
// resolvePTDownloadURL 把站点搜索结果里的"详情/获取签名"URL 解析成 qb 能直接
|
||
// 拉到 .torrent 文件的真实下载 URL。
|
||
//
|
||
// 链路:
|
||
//
|
||
// 1. 拿 URL 的 host,到 sites 表里找 base_url 同源的站点。
|
||
// 2. 如果站点的 type 是已知 PT 框架(mteam/nexusphp/unit3d/...),
|
||
// 就用对应适配器的 GetDownloadURL,传入从 URL 里 parse 出来的 id。
|
||
// 3. 任一步失败都直接返回原 URL,让 qb 自己去拉(保持向后兼容)。
|
||
//
|
||
// 这一步存在的意义:M-Team 等站点的搜索结果里 download_url 是
|
||
// /api/torrent/genDlToken?id=xxx,需要带 x-api-key 才能调用,qb 自己
|
||
// 是没法识别这种 PT 专属端点的。
|
||
func resolvePTDownloadURL(ctx context.Context, svc *service.Container, raw string, log *zap.Logger) string {
|
||
if raw == "" || svc == nil || svc.Site == nil {
|
||
return raw
|
||
}
|
||
resolved := svc.Site.ResolveDownloadURL(ctx, raw)
|
||
if resolved == raw {
|
||
return raw
|
||
}
|
||
log.Info("resolved PT download URL",
|
||
zap.String("from", raw),
|
||
zap.String("to", resolved))
|
||
return resolved
|
||
}
|
||
|
||
func addDownloadHandler(svc *service.Container) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
var req addDownloadReq
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
uid, _ := c.Get(middleware.CtxUserID)
|
||
// 把站点搜索 URL 转换成真实可下载 URL(M-Team 走 genDlToken 等)。
|
||
realURL := resolvePTDownloadURL(c.Request.Context(), svc, req.URL, svc.Log)
|
||
t, err := svc.Downloads.AddDownload(c.Request.Context(), uid.(string), realURL, req.SavePath)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
svc.Audit.Record(c.Request.Context(), uid.(string), "download.add", realURL, c.ClientIP(), "")
|
||
c.JSON(http.StatusOK, t)
|
||
}
|
||
}
|
||
|
||
func listDownloadsHandler(svc *service.Container) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
rows, live, err := svc.Downloads.List(c.Request.Context())
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"tasks": rows,
|
||
"torrents": live,
|
||
})
|
||
}
|
||
}
|
||
|
||
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 {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.Status(http.StatusNoContent)
|
||
}
|
||
}
|
||
|
||
func reloadDownloadConfigHandler(svc *service.Container) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
if err := svc.Downloads.ReloadConfig(c.Request.Context()); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.Status(http.StatusNoContent)
|
||
}
|
||
}
|