feat(storage): 实现本地存储功能,包括文件上传和下载接口

This commit is contained in:
2025-12-30 17:14:03 +08:00
parent 452cdc3f4f
commit b969218208
10 changed files with 291 additions and 27 deletions

View File

@@ -2,17 +2,23 @@ package jobs
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"time"
"github.com/riverqueue/river"
log "github.com/sirupsen/logrus"
"quyun/v2/app/jobs/args"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"quyun/v2/providers/storage"
)
// @provider(job)
type MediaProcessWorker struct {
river.WorkerDefaults[args.MediaProcessArgs]
storage *storage.Storage
}
func (j *MediaProcessWorker) Work(ctx context.Context, job *river.Job[args.MediaProcessArgs]) error {
@@ -23,14 +29,41 @@ func (j *MediaProcessWorker) Work(ctx context.Context, job *river.Job[args.Media
return err
}
// 2. Mock Processing
// Update status to processing
// 2. Update status to processing
_, err = models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.ID.Eq(asset.ID)).UpdateSimple(models.MediaAssetQuery.Status.Value(consts.MediaAssetStatusProcessing))
if err != nil {
return err
}
// 3. Update status to ready
// 3. Process Video (FFmpeg)
if asset.Type == consts.MediaAssetTypeVideo {
if _, err := exec.LookPath("ffmpeg"); err == nil {
localPath := j.storage.Config.LocalPath
if localPath == "" {
localPath = "./storage"
}
inputFile := filepath.Join(localPath, asset.ObjectKey)
outputDir := filepath.Dir(inputFile)
// Simple transcoding: convert to MP4 (mocking complex HLS for simplicity)
// Or just extract cover
coverKey := asset.ObjectKey + ".jpg"
coverFile := filepath.Join(outputDir, filepath.Base(coverKey))
// Generate Cover
cmd := exec.CommandContext(ctx, "ffmpeg", "-y", "-i", inputFile, "-ss", "00:00:01.000", "-vframes", "1", coverFile)
if out, err := cmd.CombinedOutput(); err != nil {
log.Errorf("ffmpeg failed: %s, output: %s", err, string(out))
// Don't fail the job, just skip cover
} else {
log.Infof("Generated cover: %s", coverFile)
// TODO: Create MediaAsset for cover? Or update meta?
}
} else {
log.Warn("ffmpeg not found, skipping real transcoding")
}
}
// 4. Update status to ready
_, err = models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.ID.Eq(asset.ID)).Updates(&models.MediaAsset{
Status: consts.MediaAssetStatusReady,
UpdatedAt: time.Now(),