成功添加重名文件功能

This commit is contained in:
2025-06-02 21:29:31 +08:00
parent a92d9837aa
commit 728ba79eb6
5 changed files with 345 additions and 84 deletions

View File

@@ -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 {

View File

@@ -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")
}

View File

@@ -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 = "最新添加"
}
}