Compare commits

...

3 Commits

Author SHA1 Message Date
bb374f5c52 fix: stabilize cover extraction 2026-01-23 15:46:12 +08:00
4141df7c08 fix: normalize upload types 2026-01-22 17:49:21 +08:00
bc8ed1cbbd fix: stabilize media queue processing 2026-01-22 17:44:55 +08:00
3 changed files with 49 additions and 18 deletions

View File

@@ -13,7 +13,9 @@ import (
"quyun/v2/database" "quyun/v2/database"
"quyun/v2/providers/app" "quyun/v2/providers/app"
"quyun/v2/providers/job" "quyun/v2/providers/job"
"quyun/v2/providers/jwt"
"quyun/v2/providers/postgres" "quyun/v2/providers/postgres"
"quyun/v2/providers/storage"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -24,6 +26,8 @@ func defaultProviders() container.Providers {
return commands.Default(container.Providers{ return commands.Default(container.Providers{
postgres.DefaultProvider(), postgres.DefaultProvider(),
job.DefaultProvider(), job.DefaultProvider(),
jwt.DefaultProvider(),
storage.DefaultProvider(),
database.DefaultProvider(), database.DefaultProvider(),
}...) }...)
} }

View File

@@ -53,13 +53,14 @@ func (j *MediaProcessWorker) Work(ctx context.Context, job *river.Job[args.Media
} }
// 2. 更新状态为处理中,标识处理已开始。 // 2. 更新状态为处理中,标识处理已开始。
_, err = models.MediaAssetQuery.WithContext(ctx). if err := models.MediaAssetQuery.WithContext(ctx).
Where(models.MediaAssetQuery.ID.Eq(asset.ID)). UnderlyingDB().
UpdateSimple( Model(&models.MediaAsset{}).
models.MediaAssetQuery.Status.Value(consts.MediaAssetStatusProcessing), Where("id = ?", asset.ID).
models.MediaAssetQuery.UpdatedAt.Value(time.Now()), Updates(map[string]any{
) "status": consts.MediaAssetStatusProcessing,
if err != nil { "updated_at": time.Now(),
}).Error; err != nil {
return err return err
} }
@@ -91,9 +92,13 @@ func (j *MediaProcessWorker) Work(ctx context.Context, job *river.Job[args.Media
"-i", "-i",
inputFile, inputFile,
"-ss", "-ss",
"00:00:01.000", "00:00:00.000",
"-vframes", "-vframes",
"1", "1",
"-vf",
"format=yuv420p",
"-update",
"1",
coverFile, coverFile,
) )
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
@@ -114,13 +119,17 @@ func (j *MediaProcessWorker) Work(ctx context.Context, job *river.Job[args.Media
} }
// 4. 更新最终状态。 // 4. 更新最终状态。
_, err = models.MediaAssetQuery.WithContext(ctx). if err := models.MediaAssetQuery.WithContext(ctx).
Where(models.MediaAssetQuery.ID.Eq(asset.ID)). UnderlyingDB().
Updates(&models.MediaAsset{ Model(&models.MediaAsset{}).
Status: finalStatus, Where("id = ?", asset.ID).
UpdatedAt: time.Now(), Updates(map[string]any{
}) "status": finalStatus,
return err "updated_at": time.Now(),
}).Error; err != nil {
return err
}
return nil
} }
func (j *MediaProcessWorker) registerCoverAsset(ctx context.Context, asset *models.MediaAsset, coverFile string) error { func (j *MediaProcessWorker) registerCoverAsset(ctx context.Context, asset *models.MediaAsset, coverFile string) error {

View File

@@ -2,18 +2,35 @@ import { request } from "../utils/request";
import { getTenantCode } from "../utils/tenant"; import { getTenantCode } from "../utils/tenant";
export const commonApi = { export const commonApi = {
normalizeUploadType: (type, file) => {
const normalized = (type || "").toLowerCase();
const mime = (file && file.type) || "";
if (
normalized === "video" ||
normalized === "audio" ||
normalized === "image"
) {
return normalized;
}
if (mime.startsWith("video/")) return "video";
if (mime.startsWith("audio/")) return "audio";
return "image";
},
getOptions: () => request("/common/options"), getOptions: () => request("/common/options"),
checkHash: (hash) => request(`/upload/check?hash=${hash}`), checkHash: (hash) => request(`/upload/check?hash=${hash}`),
deleteMedia: (id) => request(`/media-assets/${id}`, { method: "DELETE" }), deleteMedia: (id) => request(`/media-assets/${id}`, { method: "DELETE" }),
upload: (file, type) => { upload: (file, type) => {
const normalizedType = commonApi.normalizeUploadType(type, file);
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
formData.append("type", type); formData.append("type", normalizedType);
return request("/upload", { method: "POST", body: formData }); return request("/upload", { method: "POST", body: formData });
}, },
uploadMultipart: (file, hash, type, onProgress) => { uploadMultipart: (file, hash, type, onProgress) => {
const controller = new AbortController(); const controller = new AbortController();
const signal = controller.signal; const signal = controller.signal;
const normalizedType = commonApi.normalizeUploadType(type, file);
const promise = (async () => { const promise = (async () => {
// 1. Check Hash // 1. Check Hash
@@ -37,7 +54,7 @@ export const commonApi = {
size: file.size, size: file.size,
mime_type: file.type, mime_type: file.type,
hash: hash, hash: hash,
type: type, type: normalizedType,
}, },
signal, signal,
}); });
@@ -82,11 +99,12 @@ export const commonApi = {
return { promise, abort: () => controller.abort() }; return { promise, abort: () => controller.abort() };
}, },
uploadWithProgress: (file, type, onProgress) => { uploadWithProgress: (file, type, onProgress) => {
const normalizedType = commonApi.normalizeUploadType(type, file);
let xhr; let xhr;
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
formData.append("type", type); formData.append("type", normalizedType);
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
const tenantCode = getTenantCode(); const tenantCode = getTenantCode();