成功添加重名文件功能
This commit is contained in:
@@ -10,6 +10,98 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 重命名电影的处理函数
|
||||
func PostRename(c *gin.Context) {
|
||||
response := gin.H{
|
||||
"code": http.StatusInternalServerError,
|
||||
"message": "An unexpected error occurred.",
|
||||
}
|
||||
|
||||
// 定义请求结构体
|
||||
type RenameRequest struct {
|
||||
OldName string `json:"old_name" binding:"required"`
|
||||
NewName string `json:"new_name" binding:"required"`
|
||||
}
|
||||
|
||||
var req RenameRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response["code"] = http.StatusBadRequest
|
||||
response["message"] = "Invalid request body: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
return
|
||||
}
|
||||
|
||||
oldName := req.OldName
|
||||
newName := req.NewName
|
||||
|
||||
if oldName == "" || newName == "" {
|
||||
response["code"] = http.StatusBadRequest
|
||||
response["message"] = "Old name and new name are required."
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
return
|
||||
}
|
||||
|
||||
if oldName == newName {
|
||||
response["code"] = http.StatusBadRequest
|
||||
response["message"] = "Old name and new name cannot be the same."
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
return
|
||||
}
|
||||
|
||||
MovieDictLock.Lock()
|
||||
defer MovieDictLock.Unlock()
|
||||
|
||||
// 检查新名称是否已存在
|
||||
if _, exists := MovieDict[newName]; exists {
|
||||
response["code"] = http.StatusConflict
|
||||
response["message"] = "New name already exists."
|
||||
c.JSON(http.StatusConflict, response)
|
||||
return
|
||||
}
|
||||
|
||||
// 查找旧名称对应的电影
|
||||
movie, exists := MovieDict[oldName]
|
||||
if exists {
|
||||
// 更新电影信息
|
||||
err := movie.RenameVideo(newName) // 重命名视频文件
|
||||
if err != nil {
|
||||
response["code"] = http.StatusInternalServerError
|
||||
response["message"] = "Failed to rename video file."
|
||||
c.JSON(http.StatusInternalServerError, response)
|
||||
return
|
||||
}
|
||||
|
||||
delete(MovieDict, oldName) // 删除旧名称的条目
|
||||
MovieDict[newName] = movie // 添加新名称的条目
|
||||
|
||||
response["code"] = http.StatusOK
|
||||
response["message"] = "Movie renamed successfully."
|
||||
response["data"] = gin.H{"old_name": oldName, "new_name": newName}
|
||||
c.JSON(http.StatusOK, response)
|
||||
log.Printf("Movie renamed from %s to %s successfully", oldName, newName)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果旧名称不存在,返回404错误
|
||||
response["code"] = http.StatusNotFound
|
||||
response["message"] = "Movie not found."
|
||||
c.JSON(http.StatusNotFound, response)
|
||||
log.Printf("Movie rename failed: %s not found", oldName)
|
||||
}
|
||||
|
||||
func GetCategoryList(c *gin.Context) {
|
||||
response := gin.H{
|
||||
"code": http.StatusInternalServerError,
|
||||
"message": "An unexpected error occurred.",
|
||||
"data": []string{},
|
||||
}
|
||||
|
||||
response["code"] = http.StatusOK
|
||||
response["message"] = "Success"
|
||||
response["data"] = Categories
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// MovieList 电影列表
|
||||
func MovieList(c *gin.Context) {
|
||||
response := gin.H{
|
||||
@@ -18,27 +110,27 @@ func MovieList(c *gin.Context) {
|
||||
"data": gin.H{"items": []Movie{}, "total": 0},
|
||||
}
|
||||
|
||||
var moviesToPaginate []Movie
|
||||
var moviesToPaginate []*Movie
|
||||
categoryName := c.Query("category")
|
||||
searchQuery := c.Query("search") // 获取搜索参数
|
||||
|
||||
// === 第一步:基础过滤(类别)===
|
||||
if categoryName != "" && categoryName != "最新添加" {
|
||||
// 普通类别:直接过滤
|
||||
for _, m := range movies {
|
||||
for _, m := range Movies {
|
||||
if m.TimeCategory == categoryName {
|
||||
moviesToPaginate = append(moviesToPaginate, m)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 最新添加/所有电影:使用全局movies
|
||||
moviesToPaginate = make([]Movie, len(movies))
|
||||
copy(moviesToPaginate, movies)
|
||||
moviesToPaginate = make([]*Movie, len(Movies))
|
||||
copy(moviesToPaginate, Movies)
|
||||
}
|
||||
|
||||
// === 第二步:搜索过滤 ===
|
||||
if searchQuery != "" {
|
||||
var searchResults []Movie
|
||||
var searchResults []*Movie
|
||||
lowerSearch := strings.ToLower(searchQuery)
|
||||
|
||||
for _, m := range moviesToPaginate {
|
||||
|
||||
@@ -32,7 +32,9 @@ func main() {
|
||||
|
||||
movie := eg.Group("movie")
|
||||
movie.Use(jwtMiddleware())
|
||||
movie.GET("/", MovieList)
|
||||
movie.GET("/list/category", GetCategoryList)
|
||||
movie.GET("/list", MovieList)
|
||||
movie.POST("/rename", PostRename)
|
||||
|
||||
eg.Run("0.0.0.0:4444")
|
||||
}
|
||||
|
||||
112
server/movie.go
112
server/movie.go
@@ -12,20 +12,71 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Movie 电影信息结构
|
||||
type Movie struct {
|
||||
FileName string `json:"filename"` // 原始文件名(如 "video1.mp4")
|
||||
Image string `json:"image"` // 对应的缩略图文件名(如 "video1.png")
|
||||
Duration int `json:"duration"` // 视频时长(分钟)
|
||||
TimeCategory string `json:"time_category"` // 时长分类(如 "15min", "30min")
|
||||
VideoPath string `json:"-"` // 视频文件完整路径(用于ffmpeg)
|
||||
CreatedTime int64 `json:"created_time"` // 文件创建时间戳
|
||||
FileName string `json:"filename"` // 原始文件名(如 "video1.mp4")
|
||||
Image string `json:"image"` // 对应的缩略图文件名(如 "video1.png")
|
||||
Duration int `json:"duration"` // 视频时长(分钟)
|
||||
TimeCategory string `json:"time_category"` // 时长分类(如 "15min", "30min")
|
||||
VideoPath string `json:"-"` // 视频文件完整路径(用于ffmpeg)
|
||||
CreatedTime int64 `json:"created_time"` // 文件创建时间戳
|
||||
mu sync.Mutex // 保护视频文件的并发访问
|
||||
}
|
||||
|
||||
var movies []Movie // 存储所有电影信息的全局切片
|
||||
var IsRemakePNG = false // 是否重新生成所有PNG缩略图
|
||||
func (m *Movie) RenameVideo(newName string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if newName == m.FileName {
|
||||
return nil // 如果新名称与当前名称相同,则不需要重命名
|
||||
}
|
||||
|
||||
// 获取文件扩展名
|
||||
videoExt := filepath.Ext(m.FileName)
|
||||
imageExt := filepath.Ext(m.Image)
|
||||
|
||||
// 获取当前文件的目录
|
||||
dir := filepath.Dir(m.VideoPath)
|
||||
|
||||
// 构建新的文件名(不带扩展名)
|
||||
newBaseName := strings.TrimSuffix(newName, videoExt)
|
||||
|
||||
// 重命名视频文件
|
||||
oldVideoPath := m.VideoPath
|
||||
newVideoPath := filepath.Join(dir, newBaseName+videoExt)
|
||||
if err := os.Rename(oldVideoPath, newVideoPath); err != nil {
|
||||
return fmt.Errorf("重命名视频文件失败: %w", err)
|
||||
}
|
||||
|
||||
// 重命名缩略图文件
|
||||
oldImagePath := filepath.Join(dir, m.Image)
|
||||
newImagePath := filepath.Join(dir, newBaseName+imageExt)
|
||||
if _, err := os.Stat(oldImagePath); err == nil {
|
||||
if err := os.Rename(oldImagePath, newImagePath); err != nil {
|
||||
// 如果缩略图重命名失败,尝试回滚视频文件重命名
|
||||
_ = os.Rename(newVideoPath, oldVideoPath)
|
||||
return fmt.Errorf("重命名缩略图失败: %w", err)
|
||||
}
|
||||
m.Image = newBaseName + imageExt
|
||||
}
|
||||
|
||||
// 更新结构体信息
|
||||
m.VideoPath = newVideoPath
|
||||
m.FileName = newBaseName + videoExt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var Movies []*Movie // 存储所有电影信息的全局切片
|
||||
var MovieDict = make(map[string]*Movie) // 存储需要处理缩略图的视频字典
|
||||
var MovieDictLock sync.Mutex // 保护MovieDict的并发访问
|
||||
|
||||
var IsRemakePNG = false // 是否重新生成所有PNG缩略图
|
||||
var Categories = []string{ // 分类
|
||||
"15min", "30min", "60min", "大于60min", "最新添加"}
|
||||
|
||||
// getVideoDuration 获取视频时长(秒)
|
||||
func getVideoDuration(videoPath string) (float64, error) {
|
||||
@@ -131,7 +182,7 @@ func processVideoFile(path string, movieDict map[string]*Movie) error {
|
||||
timeCat := getTimeCategory(durationMin)
|
||||
|
||||
// 创建电影记录
|
||||
movie := Movie{
|
||||
movie := &Movie{
|
||||
FileName: filename,
|
||||
Image: baseName + ".png",
|
||||
Duration: durationMin,
|
||||
@@ -139,7 +190,8 @@ func processVideoFile(path string, movieDict map[string]*Movie) error {
|
||||
VideoPath: path,
|
||||
CreatedTime: createdTime,
|
||||
}
|
||||
movies = append(movies, movie)
|
||||
Movies = append(Movies, movie)
|
||||
MovieDict[movie.FileName] = movie // 添加到全局字典
|
||||
|
||||
// 更新缩略图字典
|
||||
if entry, exists := movieDict[baseName]; exists {
|
||||
@@ -166,34 +218,6 @@ func getTimeCategory(durationMin int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// sortMovies 对电影列表排序
|
||||
func sortMovies() {
|
||||
categoryOrder := map[string]int{
|
||||
"15min": 0,
|
||||
"30min": 1,
|
||||
"60min": 2,
|
||||
"大于60min": 3,
|
||||
"最新添加": 4, // 新增的"最新添加"类别
|
||||
}
|
||||
|
||||
sort.Slice(movies, func(i, j int) bool {
|
||||
catI := categoryOrder[movies[i].TimeCategory]
|
||||
catJ := categoryOrder[movies[j].TimeCategory]
|
||||
|
||||
if catI != catJ {
|
||||
return catI < catJ
|
||||
}
|
||||
|
||||
// 如果是recent类别,按创建时间倒序排序
|
||||
if movies[i].TimeCategory == "最新添加" {
|
||||
return movies[i].CreatedTime > movies[j].CreatedTime
|
||||
}
|
||||
|
||||
// 其他类别按时长排序
|
||||
return movies[i].Duration < movies[j].Duration
|
||||
})
|
||||
}
|
||||
|
||||
// generateThumbnail 生成单个视频缩略图
|
||||
func generateThumbnail(movie *Movie) {
|
||||
baseName := strings.TrimSuffix(movie.FileName, filepath.Ext(movie.FileName))
|
||||
@@ -244,7 +268,7 @@ func initMovie() {
|
||||
|
||||
// 创建需要处理缩略图的视频字典
|
||||
movieDict := createMovieDict(files)
|
||||
movies = make([]Movie, 0) // 重置全局电影列表
|
||||
Movies = make([]*Movie, 0) // 重置全局电影列表
|
||||
|
||||
// 遍历处理每个视频文件
|
||||
err = filepath.WalkDir("./movie", func(path string, d fs.DirEntry, err error) error {
|
||||
@@ -274,17 +298,19 @@ func initMovie() {
|
||||
}
|
||||
generateThumbnail(movie)
|
||||
}
|
||||
|
||||
log.Printf("成功初始化电影数据,共找到 %d 个视频文件", len(Movies))
|
||||
}
|
||||
|
||||
// markRecentMovies 标记最近添加的电影
|
||||
func markRecentMovies() {
|
||||
// 首先按创建时间排序
|
||||
sort.Slice(movies, func(i, j int) bool {
|
||||
return movies[i].CreatedTime > movies[j].CreatedTime
|
||||
sort.Slice(Movies, func(i, j int) bool {
|
||||
return Movies[i].CreatedTime > Movies[j].CreatedTime
|
||||
})
|
||||
|
||||
// 标记前20个最新文件为"最新添加"类别
|
||||
for i := 0; i < len(movies) && i < 20; i++ {
|
||||
movies[i].TimeCategory = "最新添加"
|
||||
for i := 0; i < len(Movies) && i < 20; i++ {
|
||||
Movies[i].TimeCategory = "最新添加"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user