Răsfoiți Sursa

添加完成初版 HTTP API 接口

Signed-off-by: allan716 <[email protected]>
allan716 3 ani în urmă
părinte
comite
cf2b1b6e34

+ 8 - 0
cmd/chinesesubfinder/main.go

@@ -7,6 +7,7 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/file_downloader"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/pre_job"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/cache_center"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/common"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/global_value"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
@@ -80,6 +81,13 @@ func main() {
 		loggerBase.Infoln("Reload Log Settings, level = Info")
 	}
 
+	// ------------------------------------------------------------------------
+	// 设置接口的 API TOKEN
+	if settings.GetSettings().ExperimentalFunction.ApiKeySettings.Enabled == true {
+		common.SetApiToken(settings.GetSettings().ExperimentalFunction.ApiKeySettings.Key)
+	} else {
+		common.SetApiToken("")
+	}
 	// 是否开启开发模式,跳过某些流程
 	//settings.GetSettings().SpeedDevMode = true
 	// ------------------------------------------------------------------------

+ 112 - 6
internal/backend/controllers/v1/api.go

@@ -1,17 +1,19 @@
 package v1
 
 import (
-	"errors"
+	"github.com/allanpk716/ChineseSubFinder/internal/dao"
+	"github.com/allanpk716/ChineseSubFinder/internal/models"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/common"
 	TTaskqueue "github.com/allanpk716/ChineseSubFinder/internal/types/task_queue"
 	"github.com/gin-gonic/gin"
 	"net/http"
+	"path/filepath"
 )
 
 // AddJobHandler 外部 API 接口添加任务的处理
-func (cb ControllerBase) AddJobHandler(c *gin.Context) {
+func (cb *ControllerBase) AddJobHandler(c *gin.Context) {
 	var err error
 	defer func() {
 		// 统一的异常处理
@@ -58,7 +60,7 @@ func (cb ControllerBase) AddJobHandler(c *gin.Context) {
 }
 
 // GetJobStatusHandler 外部 API 接口获取任务的状态
-func (cb ControllerBase) GetJobStatusHandler(c *gin.Context) {
+func (cb *ControllerBase) GetJobStatusHandler(c *gin.Context) {
 	var err error
 	defer func() {
 		// 统一的异常处理
@@ -67,13 +69,117 @@ func (cb ControllerBase) GetJobStatusHandler(c *gin.Context) {
 
 	jobID := c.DefaultQuery("job_id", "")
 	if jobID == "" {
-		err = errors.New("job_id is empty")
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "job_id is empty",
+		})
 		return
 	}
 
-	found, oneJob := cb.cronHelper.DownloadQueue.GetOneJobByID(jobID)
+	found, nowOneJob := cb.cronHelper.DownloadQueue.GetOneJobByID(jobID)
 	if found == false {
-		err = errors.New("GetOneJobByID failed, id=" + jobID)
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			JobID:   jobID,
+			Message: "job not found",
+		})
+		return
+	}
+
+	c.JSON(http.StatusOK, backend.ReplyJobThings{
+		JobID:     jobID,
+		JobStatus: nowOneJob.JobStatus,
+		Message:   "ok",
+	})
+}
+
+// AddVideoPlayedInfoHandler 外部 API 接口添加已观看视频的信息
+func (cb *ControllerBase) AddVideoPlayedInfoHandler(c *gin.Context) {
+	var err error
+	defer func() {
+		// 统一的异常处理
+		cb.ErrorProcess(c, "AddVideoPlayedInfoHandler", err)
+	}()
+
+	videoPlayedInfo := backend.ReqVideoPlayedInfo{}
+	err = c.ShouldBindJSON(&videoPlayedInfo)
+	if err != nil {
+		return
+	}
+	// 这里视频文件得要存在
+	if my_util.IsFile(videoPlayedInfo.PhysicalVideoFileFullPath) == false {
+
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "physical video file not found",
+		})
+		return
+	}
+	// 查询字幕是否存在
+	videoDirFPath := filepath.Dir(videoPlayedInfo.PhysicalVideoFileFullPath)
+	subFileFullPath := filepath.Join(videoDirFPath, videoPlayedInfo.SubName)
+	if my_util.IsFile(subFileFullPath) == false {
+
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "sub file not found",
+		})
+		return
+	}
+
+	var videoPlayedInfos []models.ThirdPartSetVideoPlayedInfo
+	dao.GetDb().Where("physical_video_file_full_path = ?", videoPlayedInfo.PhysicalVideoFileFullPath).Find(&videoPlayedInfos)
+	if len(videoPlayedInfos) == 0 {
+		// 没有则新增
+		nowVideoPlayedInfo := models.ThirdPartSetVideoPlayedInfo{
+			PhysicalVideoFileFullPath: videoPlayedInfo.PhysicalVideoFileFullPath,
+			SubName:                   videoPlayedInfo.SubName,
+		}
+		dao.GetDb().Create(&nowVideoPlayedInfo)
+	} else {
+		// 有则更新
+		videoPlayedInfos[0].SubName = videoPlayedInfo.SubName
+		dao.GetDb().Save(&videoPlayedInfos[0])
+	}
+
+	c.JSON(http.StatusOK, backend.ReplyJobThings{
+		Message: "ok",
+	})
+}
+
+// DelVideoPlayedInfoHandler 外部 API 接口删除已观看视频的信息
+func (cb *ControllerBase) DelVideoPlayedInfoHandler(c *gin.Context) {
+	var err error
+	defer func() {
+		// 统一的异常处理
+		cb.ErrorProcess(c, "DelVideoPlayedInfoHandler", err)
+	}()
+
+	videoPlayedInfo := backend.ReqVideoPlayedInfo{}
+	err = c.ShouldBindJSON(&videoPlayedInfo)
+	if err != nil {
+		return
+	}
+	// 这里视频文件得要存在
+	if my_util.IsFile(videoPlayedInfo.PhysicalVideoFileFullPath) == false {
+
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "physical video file not found",
+		})
+		return
+	}
+
+	var videoPlayedInfos []models.ThirdPartSetVideoPlayedInfo
+	dao.GetDb().Where("physical_video_file_full_path = ?", videoPlayedInfo.PhysicalVideoFileFullPath).Find(&videoPlayedInfos)
+	if len(videoPlayedInfos) == 0 {
+		// 没有则也返回成功
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "ok",
+		})
+		return
+
+	} else {
+		// 有则更新,因为这个物理路径是主键,所以不用担心会查询出多个
+		dao.GetDb().Delete(&videoPlayedInfos[0])
+		c.JSON(http.StatusOK, backend.ReplyJobThings{
+			Message: "ok",
+		})
 		return
 	}
 }

+ 9 - 0
internal/backend/controllers/v1/settings.go

@@ -1,6 +1,7 @@
 package v1
 
 import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/common"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
@@ -43,6 +44,14 @@ func (cb ControllerBase) SettingsHandler(c *gin.Context) {
 			if err != nil {
 				return
 			}
+			// ----------------------------------------
+			// 设置接口的 API TOKEN
+			if settings.GetSettings().ExperimentalFunction.ApiKeySettings.Enabled == true {
+				common.SetApiToken(settings.GetSettings().ExperimentalFunction.ApiKeySettings.Key)
+			} else {
+				common.SetApiToken("")
+			}
+			// ----------------------------------------
 			// 不管如何,都进行一次代理服务器的关闭,然后开启由具体的 获取 ProxySettings GetLocalHttpProxyUrl 操作开启这个服务器
 			err = settings.GetSettings().AdvancedSettings.ProxySettings.CloseLocalHttpProxyServer()
 			if err != nil {

+ 20 - 0
internal/backend/middle/auth.go

@@ -27,3 +27,23 @@ func CheckAuth() gin.HandlerFunc {
 		context.Next()
 	}
 }
+
+func CheckApiAuth() gin.HandlerFunc {
+
+	return func(context *gin.Context) {
+		authHeader := context.Request.Header.Get("Authorization")
+		if len(authHeader) <= 1 {
+			context.JSON(http.StatusUnauthorized, backend.ReplyCheckAuth{Message: "Request Header Authorization Error"})
+			context.Abort()
+			return
+		}
+		nowAccessToken := strings.Fields(authHeader)[1]
+		if nowAccessToken == "" || nowAccessToken != common.GetApiToken() {
+			context.JSON(http.StatusUnauthorized, backend.ReplyCheckAuth{Message: "AccessToken Error"})
+			context.Abort()
+			return
+		}
+		// 向下传递消息
+		context.Next()
+	}
+}

+ 11 - 0
internal/backend/routers/base_router.go

@@ -57,5 +57,16 @@ func InitRouter(fileDownloader *file_downloader.FileDownloader, router *gin.Engi
 		GroupV1.POST("/video/list/add", cbV1.VideoListAddHandler)
 	}
 
+	GroupAPIV1 := router.Group("/api/v1")
+	{
+		GroupAPIV1.Use(middle.CheckApiAuth())
+
+		GroupAPIV1.POST("/add-job", cbV1.AddJobHandler)
+		GroupAPIV1.GET("/job-status", cbV1.GetJobStatusHandler)
+		GroupAPIV1.POST("/change-job-status", cbV1.ChangeJobStatusHandler)
+		GroupAPIV1.POST("/add-video-played-info", cbV1.AddVideoPlayedInfoHandler)
+		GroupAPIV1.DELETE("/del-video-played-info", cbV1.DelVideoPlayedInfoHandler)
+	}
+
 	return cbV1
 }

+ 2 - 1
internal/dao/init.go

@@ -66,7 +66,8 @@ func InitDb() error {
 	}
 	// 迁移 schema
 	err = db.AutoMigrate(&models.HotFix{}, &models.SubFormatRec{},
-		&models.IMDBInfo{}, &models.VideoSubInfo{})
+		&models.IMDBInfo{}, &models.VideoSubInfo{},
+		&models.ThirdPartSetVideoPlayedInfo{})
 	if err != nil {
 		return errors.New(fmt.Sprintf("db AutoMigrate error, %s", err.Error()))
 	}

+ 6 - 0
internal/models/third_part_video_played_info.go

@@ -0,0 +1,6 @@
+package models
+
+type ThirdPartSetVideoPlayedInfo struct {
+	PhysicalVideoFileFullPath string `gorm:"primaryKey" json:"physical_video_file_full_path"` // 视频的物理路径
+	SubName                   string `json:"sub_name"`                                        // 字幕的名称,需要配合视频进行推算其的文件位置
+}

+ 19 - 0
internal/pkg/common/global_value.go

@@ -4,6 +4,7 @@ import (
 	"sync"
 )
 
+// SetAccessToken 设置 Web UI 访问的 Token
 func SetAccessToken(newToken string) {
 
 	defer mutexAccessToken.Unlock()
@@ -11,6 +12,7 @@ func SetAccessToken(newToken string) {
 	accessToken = newToken
 }
 
+// GetAccessToken 获取 Web UI 访问的 Token
 func GetAccessToken() string {
 
 	defer mutexAccessToken.Unlock()
@@ -18,7 +20,24 @@ func GetAccessToken() string {
 	return accessToken
 }
 
+// SetApiToken 设置 API 接口访问的 Token
+func SetApiToken(newToken string) {
+
+	defer mutexAccessToken.Unlock()
+	mutexAccessToken.Lock()
+	apiToken = newToken
+}
+
+// GetApiToken 获取 API 接口访问的 Token
+func GetApiToken() string {
+
+	defer mutexAccessToken.Unlock()
+	mutexAccessToken.Lock()
+	return apiToken
+}
+
 var (
 	accessToken      = ""
+	apiToken         = ""
 	mutexAccessToken sync.Mutex
 )

+ 9 - 0
internal/types/backend/reply_job_things.go

@@ -0,0 +1,9 @@
+package backend
+
+import "github.com/allanpk716/ChineseSubFinder/internal/types/task_queue"
+
+type ReplyJobThings struct {
+	JobID     string               `json:"job_id"`
+	JobStatus task_queue.JobStatus `json:"job_status"`
+	Message   string               `json:"message"`
+}

+ 5 - 0
internal/types/backend/req_job_things.go

@@ -0,0 +1,5 @@
+package backend
+
+type ReqJobThings struct {
+	JobID string `json:"job_id"`
+}

+ 6 - 0
internal/types/backend/req_video_played_info.go

@@ -0,0 +1,6 @@
+package backend
+
+type ReqVideoPlayedInfo struct {
+	PhysicalVideoFileFullPath string `json:"physical_video_file_full_path"` // 视频的物理路径
+	SubName                   string `json:"sub_name"`                      // 字幕的名称,不要传递全路径
+}