88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package jobs
|
|
|
|
import (
|
|
"context"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"quyun/v2/app/jobs/args"
|
|
"quyun/v2/database/models"
|
|
"quyun/v2/pkg/consts"
|
|
"quyun/v2/providers/storage"
|
|
|
|
"github.com/riverqueue/river"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// @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 {
|
|
arg := job.Args
|
|
// 1. Fetch Asset
|
|
asset, err := models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.ID.Eq(arg.AssetID)).First()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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. 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(),
|
|
})
|
|
return err
|
|
}
|