From 0b7cd054862a10699934ec3c0c4622754f0631e8 Mon Sep 17 00:00:00 2001 From: lzw-723 Date: Mon, 6 Apr 2026 23:16:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=AD=8C=E6=9B=B2=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 10 ++++++++++ internal/controller/song.go | 15 +++++++++++++++ internal/service/song_svc.go | 14 ++++++++++++++ internal/util/ffmpeg.go | 20 ++++++++++++++++++++ main.go | 5 ++++- 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 internal/util/ffmpeg.go diff --git a/config/config.go b/config/config.go index b81ee63..b716e50 100644 --- a/config/config.go +++ b/config/config.go @@ -3,22 +3,32 @@ package config import ( "log" "os" + "path" "strconv" ) type Config struct { DatabasePath string + CachePath string } // LoadConfig loads configuration from environment variables func LoadConfig() *Config { cfg := &Config{ DatabasePath: getEnv("DATABASE_PATH", "./butterfliu.db"), + CachePath: getEnv("CACHE_PATH", "./cache"), } + os.MkdirAll(cfg.CachePath, os.ModeAppend) return cfg } +func (self *Config) GetCachePath(cacheType string) string { + path := path.Join(self.CachePath, cacheType) + os.MkdirAll(path, os.ModeAppend) + return path +} + // getEnv gets an environment variable with a default value func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { diff --git a/internal/controller/song.go b/internal/controller/song.go index f3e113a..aeb6ed4 100644 --- a/internal/controller/song.go +++ b/internal/controller/song.go @@ -41,3 +41,18 @@ func (c *SongController) Stream(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, mediaFile.Path) } + +func (c *SongController) Cover(w http.ResponseWriter, r *http.Request) { + paramID := chi.URLParam(r, "id") + id, err := strconv.Atoi(paramID) + if err != nil { + http.Error(w, "无效的歌曲 ID", http.StatusBadRequest) + return + } + coverPath, err := c.service.GetCover(id) + if err != nil { + http.Error(w, "获取封面异常", http.StatusNotFound) + return + } + http.ServeFile(w, r, coverPath) +} diff --git a/internal/service/song_svc.go b/internal/service/song_svc.go index d33e327..5f2b853 100644 --- a/internal/service/song_svc.go +++ b/internal/service/song_svc.go @@ -1,8 +1,12 @@ package service import ( + "butterfliu/config" "butterfliu/internal/model" "butterfliu/internal/repository" + "butterfliu/internal/util" + "path" + "strconv" ) type SongService struct { @@ -29,3 +33,13 @@ func (s *SongService) GetMediaFile(id int) (model.MediaFile, error) { } return s.mediaRepo.Get(song.MediaFileID) } + +func (s *SongService) GetCover(id int) (string, error) { + file, err := s.GetMediaFile(id) + if err != nil { + return "", err + } + util.ExtractCover(file.Path, id) + conf := config.LoadConfig() + return path.Join(conf.GetCachePath("cover"), strconv.Itoa(id)+".jpg"), nil +} diff --git a/internal/util/ffmpeg.go b/internal/util/ffmpeg.go new file mode 100644 index 0000000..91e150f --- /dev/null +++ b/internal/util/ffmpeg.go @@ -0,0 +1,20 @@ +package util + +import ( + "os/exec" + "strconv" +) + +func ExtractCover(filePath string, id int) bool { + if _, err := exec.LookPath("ffmpeg"); err != nil { + return false + } + coverPath := "cache/cover/" + strconv.Itoa(id) + ".jpg" + cmd := exec.Command("ffmpeg", "-i", filePath, "-an", "-vcodec", "mjpeg", "-q:v", "5", "-f", "image2", "-y", coverPath) + if err := cmd.Run(); err != nil { + return false + } + + return true + +} diff --git a/main.go b/main.go index a5d4b1f..52ad768 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "butterfliu/internal/controller" "butterfliu/internal/repository" "butterfliu/internal/service" + "butterfliu/config" "log" "net/http" @@ -12,7 +13,8 @@ import ( ) func main() { - err := InitDB("./butterfliu.db") + config := config.LoadConfig() + err := InitDB(config.DatabasePath) if err != nil { log.Fatal(err) } @@ -46,6 +48,7 @@ func main() { r.Route("/api/songs", func(r chi.Router) { r.Get("/", songController.GetAllWithDetails) r.Get("/{id}/stream", songController.Stream) + r.Get("/{id}/cover", songController.Cover) }) r.Route("/api", func(r chi.Router) {