feat(media): deferred image compression subsystem (APP-101)

Adds a media post-processing pipeline that compresses images off the upload
path, in batches, to save storage:

- New media_process_task ent table (media_type discriminator; image now,
  video later) + inventory client with idempotent Enqueue/ListPending.
- CompleteUpload enqueues the primary entity when the global switch and the
  owner's auto_compress_images opt-in are on and mime/size gates pass.
- A configurable cron enqueues a bounded batch onto a dedicated
  MediaProcessQueue (default 1 worker) that compresses each blob via vips
  (default) or ffmpeg using EntitySource, then writes the result back as a
  new version through manager.Update (quota handled by StorageDiff). A
  context guard prevents the write-back from re-enqueueing itself.
- BackendVersion 4.14.1 -> 4.14.2 to fire the migration; new default
  settings are idempotently backfilled.

BusyWorkers/metrics expose the queue in /admin/task; result mode 'version'
is the MVP (replace/auto are follow-ups).
pull/3493/head
thotenn 3 weeks ago
parent 8bad06a372
commit 51c7654b03

@ -108,6 +108,7 @@ func (s *server) Start() error {
s.dep.EntityRecycleQueue(context.Background()).Start()
s.dep.IoIntenseQueue(context.Background()).Start()
s.dep.RemoteDownloadQueue(context.Background()).Start()
s.dep.MediaProcessQueue(context.Background()).Start()
// Start cron jobs
c, err := crontab.NewCron(context.Background(), s.dep)

@ -5,7 +5,9 @@ package constants
// BackendVersion 当前后端版本号
// Bumped to 4.14.1 in this fork to trigger the schema migration that creates the
// group⇄storage_policy allowed-set join table and backfills it (see inventory/migration.go).
var BackendVersion = "4.14.1"
// Bumped to 4.14.2 (APP-101) to trigger the migration that creates the
// media_process_task table and seeds the media-compression default settings.
var BackendVersion = "4.14.2"
// IsPro 是否为Pro版本
var IsPro = "false"

@ -120,6 +120,10 @@ type Dep interface {
ThumbQueue(ctx context.Context) queue.Queue
// EntityRecycleQueue Get a singleton queue.Queue instance for entity recycle.
EntityRecycleQueue(ctx context.Context) queue.Queue
// MediaProcessQueue Get a singleton queue.Queue instance for media post-processing (APP-101).
MediaProcessQueue(ctx context.Context) queue.Queue
// MediaProcessClient Creates a new inventory.MediaProcessClient instance for the media_process_task store.
MediaProcessClient() inventory.MediaProcessClient
// MimeDetector Get a singleton fs.MimeDetector instance for MIME type detection.
MimeDetector(ctx context.Context) mime.MimeDetector
// CredManager Get a singleton credmanager.CredManager instance for credential management.
@ -165,6 +169,7 @@ type dependency struct {
groupClient inventory.GroupClient
storagePolicyClient inventory.StoragePolicyClient
taskClient inventory.TaskClient
mediaProcessClient inventory.MediaProcessClient
nodeClient inventory.NodeClient
davAccountClient inventory.DavAccountClient
directLinkClient inventory.DirectLinkClient
@ -180,6 +185,7 @@ type dependency struct {
thumbQueue queue.Queue
mediaMetaQueue queue.Queue
entityRecycleQueue queue.Queue
mediaProcessQueue queue.Queue
slaveQueue queue.Queue
remoteDownloadQueue queue.Queue
ioIntenseQueueTask queue.Task
@ -746,6 +752,47 @@ func (d *dependency) EntityRecycleQueue(ctx context.Context) queue.Queue {
return d.entityRecycleQueue
}
func (d *dependency) MediaProcessQueue(ctx context.Context) queue.Queue {
d.mu.Lock()
defer d.mu.Unlock()
_, reload := ctx.Value(ReloadCtx{}).(bool)
if d.mediaProcessQueue != nil && !reload {
return d.mediaProcessQueue
}
if d.mediaProcessQueue != nil {
d.mediaProcessQueue.Shutdown()
}
settings := d.SettingProvider()
queueSetting := settings.Queue(context.Background(), setting.QueueTypeMediaProcess)
// Worker count comes from the dedicated media_compress_worker_num key (default
// 1) to keep compression CPU-bounded — the generic queue worker_num getter has
// an upstream key bug that always yields its fallback.
workerNum := settings.MediaProcess(context.Background()).WorkerNum
d.mediaProcessQueue = queue.New(d.Logger(), d.TaskClient(), d.TaskRegistry(), d,
queue.WithBackoffFactor(queueSetting.BackoffFactor),
queue.WithMaxRetry(queueSetting.MaxRetry),
queue.WithBackoffMaxDuration(queueSetting.BackoffMaxDuration),
queue.WithRetryDelay(queueSetting.RetryDelay),
queue.WithWorkerCount(workerNum),
queue.WithName("MediaProcessQueue"),
queue.WithMaxTaskExecution(queueSetting.MaxExecution),
queue.WithResumeTaskType(queue.MediaCompressTaskType),
queue.WithTaskPullInterval(10*time.Second),
)
return d.mediaProcessQueue
}
func (d *dependency) MediaProcessClient() inventory.MediaProcessClient {
if d.mediaProcessClient != nil {
return d.mediaProcessClient
}
return inventory.NewMediaProcessClient(d.DBClient(), d.ConfigProvider().Database().Type)
}
func (d *dependency) SlaveQueue(ctx context.Context) queue.Queue {
d.mu.Lock()
defer d.mu.Unlock()

@ -1 +1 @@
Subproject commit 4107a8f4fd66e95a26877ee35b86ac6413976d4a
Subproject commit 74d41bbcee74020f3f071340f8cea73152f705c9

@ -21,6 +21,7 @@ import (
"github.com/cloudreve/Cloudreve/v4/ent/file"
"github.com/cloudreve/Cloudreve/v4/ent/fsevent"
"github.com/cloudreve/Cloudreve/v4/ent/group"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/metadata"
"github.com/cloudreve/Cloudreve/v4/ent/node"
"github.com/cloudreve/Cloudreve/v4/ent/oauthclient"
@ -52,6 +53,8 @@ type Client struct {
FsEvent *FsEventClient
// Group is the client for interacting with the Group builders.
Group *GroupClient
// MediaProcessTask is the client for interacting with the MediaProcessTask builders.
MediaProcessTask *MediaProcessTaskClient
// Metadata is the client for interacting with the Metadata builders.
Metadata *MetadataClient
// Node is the client for interacting with the Node builders.
@ -89,6 +92,7 @@ func (c *Client) init() {
c.File = NewFileClient(c.config)
c.FsEvent = NewFsEventClient(c.config)
c.Group = NewGroupClient(c.config)
c.MediaProcessTask = NewMediaProcessTaskClient(c.config)
c.Metadata = NewMetadataClient(c.config)
c.Node = NewNodeClient(c.config)
c.OAuthClient = NewOAuthClientClient(c.config)
@ -189,24 +193,25 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
cfg := c.config
cfg.driver = tx
return &Tx{
ctx: ctx,
config: cfg,
DavAccount: NewDavAccountClient(cfg),
DirectLink: NewDirectLinkClient(cfg),
Entity: NewEntityClient(cfg),
File: NewFileClient(cfg),
FsEvent: NewFsEventClient(cfg),
Group: NewGroupClient(cfg),
Metadata: NewMetadataClient(cfg),
Node: NewNodeClient(cfg),
OAuthClient: NewOAuthClientClient(cfg),
OAuthGrant: NewOAuthGrantClient(cfg),
Passkey: NewPasskeyClient(cfg),
Setting: NewSettingClient(cfg),
Share: NewShareClient(cfg),
StoragePolicy: NewStoragePolicyClient(cfg),
Task: NewTaskClient(cfg),
User: NewUserClient(cfg),
ctx: ctx,
config: cfg,
DavAccount: NewDavAccountClient(cfg),
DirectLink: NewDirectLinkClient(cfg),
Entity: NewEntityClient(cfg),
File: NewFileClient(cfg),
FsEvent: NewFsEventClient(cfg),
Group: NewGroupClient(cfg),
MediaProcessTask: NewMediaProcessTaskClient(cfg),
Metadata: NewMetadataClient(cfg),
Node: NewNodeClient(cfg),
OAuthClient: NewOAuthClientClient(cfg),
OAuthGrant: NewOAuthGrantClient(cfg),
Passkey: NewPasskeyClient(cfg),
Setting: NewSettingClient(cfg),
Share: NewShareClient(cfg),
StoragePolicy: NewStoragePolicyClient(cfg),
Task: NewTaskClient(cfg),
User: NewUserClient(cfg),
}, nil
}
@ -224,24 +229,25 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{
ctx: ctx,
config: cfg,
DavAccount: NewDavAccountClient(cfg),
DirectLink: NewDirectLinkClient(cfg),
Entity: NewEntityClient(cfg),
File: NewFileClient(cfg),
FsEvent: NewFsEventClient(cfg),
Group: NewGroupClient(cfg),
Metadata: NewMetadataClient(cfg),
Node: NewNodeClient(cfg),
OAuthClient: NewOAuthClientClient(cfg),
OAuthGrant: NewOAuthGrantClient(cfg),
Passkey: NewPasskeyClient(cfg),
Setting: NewSettingClient(cfg),
Share: NewShareClient(cfg),
StoragePolicy: NewStoragePolicyClient(cfg),
Task: NewTaskClient(cfg),
User: NewUserClient(cfg),
ctx: ctx,
config: cfg,
DavAccount: NewDavAccountClient(cfg),
DirectLink: NewDirectLinkClient(cfg),
Entity: NewEntityClient(cfg),
File: NewFileClient(cfg),
FsEvent: NewFsEventClient(cfg),
Group: NewGroupClient(cfg),
MediaProcessTask: NewMediaProcessTaskClient(cfg),
Metadata: NewMetadataClient(cfg),
Node: NewNodeClient(cfg),
OAuthClient: NewOAuthClientClient(cfg),
OAuthGrant: NewOAuthGrantClient(cfg),
Passkey: NewPasskeyClient(cfg),
Setting: NewSettingClient(cfg),
Share: NewShareClient(cfg),
StoragePolicy: NewStoragePolicyClient(cfg),
Task: NewTaskClient(cfg),
User: NewUserClient(cfg),
}, nil
}
@ -271,9 +277,9 @@ func (c *Client) Close() error {
// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{
c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group, c.Metadata,
c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey, c.Setting, c.Share,
c.StoragePolicy, c.Task, c.User,
c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group,
c.MediaProcessTask, c.Metadata, c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey,
c.Setting, c.Share, c.StoragePolicy, c.Task, c.User,
} {
n.Use(hooks...)
}
@ -283,9 +289,9 @@ func (c *Client) Use(hooks ...Hook) {
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{
c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group, c.Metadata,
c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey, c.Setting, c.Share,
c.StoragePolicy, c.Task, c.User,
c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group,
c.MediaProcessTask, c.Metadata, c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey,
c.Setting, c.Share, c.StoragePolicy, c.Task, c.User,
} {
n.Intercept(interceptors...)
}
@ -306,6 +312,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.FsEvent.mutate(ctx, m)
case *GroupMutation:
return c.Group.mutate(ctx, m)
case *MediaProcessTaskMutation:
return c.MediaProcessTask.mutate(ctx, m)
case *MetadataMutation:
return c.Metadata.mutate(ctx, m)
case *NodeMutation:
@ -1412,6 +1420,141 @@ func (c *GroupClient) mutate(ctx context.Context, m *GroupMutation) (Value, erro
}
}
// MediaProcessTaskClient is a client for the MediaProcessTask schema.
type MediaProcessTaskClient struct {
config
}
// NewMediaProcessTaskClient returns a client for the MediaProcessTask from the given config.
func NewMediaProcessTaskClient(c config) *MediaProcessTaskClient {
return &MediaProcessTaskClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `mediaprocesstask.Hooks(f(g(h())))`.
func (c *MediaProcessTaskClient) Use(hooks ...Hook) {
c.hooks.MediaProcessTask = append(c.hooks.MediaProcessTask, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `mediaprocesstask.Intercept(f(g(h())))`.
func (c *MediaProcessTaskClient) Intercept(interceptors ...Interceptor) {
c.inters.MediaProcessTask = append(c.inters.MediaProcessTask, interceptors...)
}
// Create returns a builder for creating a MediaProcessTask entity.
func (c *MediaProcessTaskClient) Create() *MediaProcessTaskCreate {
mutation := newMediaProcessTaskMutation(c.config, OpCreate)
return &MediaProcessTaskCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of MediaProcessTask entities.
func (c *MediaProcessTaskClient) CreateBulk(builders ...*MediaProcessTaskCreate) *MediaProcessTaskCreateBulk {
return &MediaProcessTaskCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *MediaProcessTaskClient) MapCreateBulk(slice any, setFunc func(*MediaProcessTaskCreate, int)) *MediaProcessTaskCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &MediaProcessTaskCreateBulk{err: fmt.Errorf("calling to MediaProcessTaskClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*MediaProcessTaskCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &MediaProcessTaskCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for MediaProcessTask.
func (c *MediaProcessTaskClient) Update() *MediaProcessTaskUpdate {
mutation := newMediaProcessTaskMutation(c.config, OpUpdate)
return &MediaProcessTaskUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *MediaProcessTaskClient) UpdateOne(mpt *MediaProcessTask) *MediaProcessTaskUpdateOne {
mutation := newMediaProcessTaskMutation(c.config, OpUpdateOne, withMediaProcessTask(mpt))
return &MediaProcessTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *MediaProcessTaskClient) UpdateOneID(id int) *MediaProcessTaskUpdateOne {
mutation := newMediaProcessTaskMutation(c.config, OpUpdateOne, withMediaProcessTaskID(id))
return &MediaProcessTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for MediaProcessTask.
func (c *MediaProcessTaskClient) Delete() *MediaProcessTaskDelete {
mutation := newMediaProcessTaskMutation(c.config, OpDelete)
return &MediaProcessTaskDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *MediaProcessTaskClient) DeleteOne(mpt *MediaProcessTask) *MediaProcessTaskDeleteOne {
return c.DeleteOneID(mpt.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *MediaProcessTaskClient) DeleteOneID(id int) *MediaProcessTaskDeleteOne {
builder := c.Delete().Where(mediaprocesstask.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &MediaProcessTaskDeleteOne{builder}
}
// Query returns a query builder for MediaProcessTask.
func (c *MediaProcessTaskClient) Query() *MediaProcessTaskQuery {
return &MediaProcessTaskQuery{
config: c.config,
ctx: &QueryContext{Type: TypeMediaProcessTask},
inters: c.Interceptors(),
}
}
// Get returns a MediaProcessTask entity by its id.
func (c *MediaProcessTaskClient) Get(ctx context.Context, id int) (*MediaProcessTask, error) {
return c.Query().Where(mediaprocesstask.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *MediaProcessTaskClient) GetX(ctx context.Context, id int) *MediaProcessTask {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// Hooks returns the client hooks.
func (c *MediaProcessTaskClient) Hooks() []Hook {
hooks := c.hooks.MediaProcessTask
return append(hooks[:len(hooks):len(hooks)], mediaprocesstask.Hooks[:]...)
}
// Interceptors returns the client interceptors.
func (c *MediaProcessTaskClient) Interceptors() []Interceptor {
inters := c.inters.MediaProcessTask
return append(inters[:len(inters):len(inters)], mediaprocesstask.Interceptors[:]...)
}
func (c *MediaProcessTaskClient) mutate(ctx context.Context, m *MediaProcessTaskMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&MediaProcessTaskCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&MediaProcessTaskUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&MediaProcessTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&MediaProcessTaskDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown MediaProcessTask mutation op: %q", m.Op())
}
}
// MetadataClient is a client for the Metadata schema.
type MetadataClient struct {
config
@ -3133,14 +3276,14 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error)
// hooks and interceptors per client, for fast access.
type (
hooks struct {
DavAccount, DirectLink, Entity, File, FsEvent, Group, Metadata, Node,
OAuthClient, OAuthGrant, Passkey, Setting, Share, StoragePolicy, Task,
User []ent.Hook
DavAccount, DirectLink, Entity, File, FsEvent, Group, MediaProcessTask,
Metadata, Node, OAuthClient, OAuthGrant, Passkey, Setting, Share,
StoragePolicy, Task, User []ent.Hook
}
inters struct {
DavAccount, DirectLink, Entity, File, FsEvent, Group, Metadata, Node,
OAuthClient, OAuthGrant, Passkey, Setting, Share, StoragePolicy, Task,
User []ent.Interceptor
DavAccount, DirectLink, Entity, File, FsEvent, Group, MediaProcessTask,
Metadata, Node, OAuthClient, OAuthGrant, Passkey, Setting, Share,
StoragePolicy, Task, User []ent.Interceptor
}
)

@ -18,6 +18,7 @@ import (
"github.com/cloudreve/Cloudreve/v4/ent/file"
"github.com/cloudreve/Cloudreve/v4/ent/fsevent"
"github.com/cloudreve/Cloudreve/v4/ent/group"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/metadata"
"github.com/cloudreve/Cloudreve/v4/ent/node"
"github.com/cloudreve/Cloudreve/v4/ent/oauthclient"
@ -88,22 +89,23 @@ var (
func checkColumn(table, column string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
davaccount.Table: davaccount.ValidColumn,
directlink.Table: directlink.ValidColumn,
entity.Table: entity.ValidColumn,
file.Table: file.ValidColumn,
fsevent.Table: fsevent.ValidColumn,
group.Table: group.ValidColumn,
metadata.Table: metadata.ValidColumn,
node.Table: node.ValidColumn,
oauthclient.Table: oauthclient.ValidColumn,
oauthgrant.Table: oauthgrant.ValidColumn,
passkey.Table: passkey.ValidColumn,
setting.Table: setting.ValidColumn,
share.Table: share.ValidColumn,
storagepolicy.Table: storagepolicy.ValidColumn,
task.Table: task.ValidColumn,
user.Table: user.ValidColumn,
davaccount.Table: davaccount.ValidColumn,
directlink.Table: directlink.ValidColumn,
entity.Table: entity.ValidColumn,
file.Table: file.ValidColumn,
fsevent.Table: fsevent.ValidColumn,
group.Table: group.ValidColumn,
mediaprocesstask.Table: mediaprocesstask.ValidColumn,
metadata.Table: metadata.ValidColumn,
node.Table: node.ValidColumn,
oauthclient.Table: oauthclient.ValidColumn,
oauthgrant.Table: oauthgrant.ValidColumn,
passkey.Table: passkey.ValidColumn,
setting.Table: setting.ValidColumn,
share.Table: share.ValidColumn,
storagepolicy.Table: storagepolicy.ValidColumn,
task.Table: task.ValidColumn,
user.Table: user.ValidColumn,
})
})
return columnCheck(table, column)

@ -81,6 +81,18 @@ func (f GroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GroupMutation", m)
}
// The MediaProcessTaskFunc type is an adapter to allow the use of ordinary
// function as MediaProcessTask mutator.
type MediaProcessTaskFunc func(context.Context, *ent.MediaProcessTaskMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f MediaProcessTaskFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.MediaProcessTaskMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MediaProcessTaskMutation", m)
}
// The MetadataFunc type is an adapter to allow the use of ordinary
// function as Metadata mutator.
type MetadataFunc func(context.Context, *ent.MetadataMutation) (ent.Value, error)

@ -14,6 +14,7 @@ import (
"github.com/cloudreve/Cloudreve/v4/ent/file"
"github.com/cloudreve/Cloudreve/v4/ent/fsevent"
"github.com/cloudreve/Cloudreve/v4/ent/group"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/metadata"
"github.com/cloudreve/Cloudreve/v4/ent/node"
"github.com/cloudreve/Cloudreve/v4/ent/oauthclient"
@ -245,6 +246,33 @@ func (f TraverseGroup) Traverse(ctx context.Context, q ent.Query) error {
return fmt.Errorf("unexpected query type %T. expect *ent.GroupQuery", q)
}
// The MediaProcessTaskFunc type is an adapter to allow the use of ordinary function as a Querier.
type MediaProcessTaskFunc func(context.Context, *ent.MediaProcessTaskQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f MediaProcessTaskFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.MediaProcessTaskQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.MediaProcessTaskQuery", q)
}
// The TraverseMediaProcessTask type is an adapter to allow the use of ordinary function as Traverser.
type TraverseMediaProcessTask func(context.Context, *ent.MediaProcessTaskQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseMediaProcessTask) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseMediaProcessTask) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.MediaProcessTaskQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.MediaProcessTaskQuery", q)
}
// The MetadataFunc type is an adapter to allow the use of ordinary function as a Querier.
type MetadataFunc func(context.Context, *ent.MetadataQuery) (ent.Value, error)
@ -530,6 +558,8 @@ func NewQuery(q ent.Query) (Query, error) {
return &query[*ent.FsEventQuery, predicate.FsEvent, fsevent.OrderOption]{typ: ent.TypeFsEvent, tq: q}, nil
case *ent.GroupQuery:
return &query[*ent.GroupQuery, predicate.Group, group.OrderOption]{typ: ent.TypeGroup, tq: q}, nil
case *ent.MediaProcessTaskQuery:
return &query[*ent.MediaProcessTaskQuery, predicate.MediaProcessTask, mediaprocesstask.OrderOption]{typ: ent.TypeMediaProcessTask, tq: q}, nil
case *ent.MetadataQuery:
return &query[*ent.MetadataQuery, predicate.Metadata, metadata.OrderOption]{typ: ent.TypeMetadata, tq: q}, nil
case *ent.NodeQuery:

File diff suppressed because one or more lines are too long

@ -0,0 +1,236 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"encoding/json"
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// MediaProcessTask is the model entity for the MediaProcessTask schema.
type MediaProcessTask struct {
config `json:"-"`
// ID of the ent.
ID int `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// DeletedAt holds the value of the "deleted_at" field.
DeletedAt *time.Time `json:"deleted_at,omitempty"`
// MediaType holds the value of the "media_type" field.
MediaType mediaprocesstask.MediaType `json:"media_type,omitempty"`
// Status holds the value of the "status" field.
Status mediaprocesstask.Status `json:"status,omitempty"`
// EntityID holds the value of the "entity_id" field.
EntityID int `json:"entity_id,omitempty"`
// FileID holds the value of the "file_id" field.
FileID int `json:"file_id,omitempty"`
// OwnerID holds the value of the "owner_id" field.
OwnerID int `json:"owner_id,omitempty"`
// Attempts holds the value of the "attempts" field.
Attempts int `json:"attempts,omitempty"`
// Error holds the value of the "error" field.
Error string `json:"error,omitempty"`
// ResultSize holds the value of the "result_size" field.
ResultSize int64 `json:"result_size,omitempty"`
// Props holds the value of the "props" field.
Props *types.MediaProcessTaskProps `json:"props,omitempty"`
selectValues sql.SelectValues
}
// scanValues returns the types for scanning values from sql.Rows.
func (*MediaProcessTask) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case mediaprocesstask.FieldProps:
values[i] = new([]byte)
case mediaprocesstask.FieldID, mediaprocesstask.FieldEntityID, mediaprocesstask.FieldFileID, mediaprocesstask.FieldOwnerID, mediaprocesstask.FieldAttempts, mediaprocesstask.FieldResultSize:
values[i] = new(sql.NullInt64)
case mediaprocesstask.FieldMediaType, mediaprocesstask.FieldStatus, mediaprocesstask.FieldError:
values[i] = new(sql.NullString)
case mediaprocesstask.FieldCreatedAt, mediaprocesstask.FieldUpdatedAt, mediaprocesstask.FieldDeletedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the MediaProcessTask fields.
func (mpt *MediaProcessTask) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case mediaprocesstask.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
mpt.ID = int(value.Int64)
case mediaprocesstask.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
mpt.CreatedAt = value.Time
}
case mediaprocesstask.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
mpt.UpdatedAt = value.Time
}
case mediaprocesstask.FieldDeletedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
} else if value.Valid {
mpt.DeletedAt = new(time.Time)
*mpt.DeletedAt = value.Time
}
case mediaprocesstask.FieldMediaType:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field media_type", values[i])
} else if value.Valid {
mpt.MediaType = mediaprocesstask.MediaType(value.String)
}
case mediaprocesstask.FieldStatus:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field status", values[i])
} else if value.Valid {
mpt.Status = mediaprocesstask.Status(value.String)
}
case mediaprocesstask.FieldEntityID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field entity_id", values[i])
} else if value.Valid {
mpt.EntityID = int(value.Int64)
}
case mediaprocesstask.FieldFileID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field file_id", values[i])
} else if value.Valid {
mpt.FileID = int(value.Int64)
}
case mediaprocesstask.FieldOwnerID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field owner_id", values[i])
} else if value.Valid {
mpt.OwnerID = int(value.Int64)
}
case mediaprocesstask.FieldAttempts:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field attempts", values[i])
} else if value.Valid {
mpt.Attempts = int(value.Int64)
}
case mediaprocesstask.FieldError:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field error", values[i])
} else if value.Valid {
mpt.Error = value.String
}
case mediaprocesstask.FieldResultSize:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field result_size", values[i])
} else if value.Valid {
mpt.ResultSize = value.Int64
}
case mediaprocesstask.FieldProps:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field props", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &mpt.Props); err != nil {
return fmt.Errorf("unmarshal field props: %w", err)
}
}
default:
mpt.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the MediaProcessTask.
// This includes values selected through modifiers, order, etc.
func (mpt *MediaProcessTask) Value(name string) (ent.Value, error) {
return mpt.selectValues.Get(name)
}
// Update returns a builder for updating this MediaProcessTask.
// Note that you need to call MediaProcessTask.Unwrap() before calling this method if this MediaProcessTask
// was returned from a transaction, and the transaction was committed or rolled back.
func (mpt *MediaProcessTask) Update() *MediaProcessTaskUpdateOne {
return NewMediaProcessTaskClient(mpt.config).UpdateOne(mpt)
}
// Unwrap unwraps the MediaProcessTask entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (mpt *MediaProcessTask) Unwrap() *MediaProcessTask {
_tx, ok := mpt.config.driver.(*txDriver)
if !ok {
panic("ent: MediaProcessTask is not a transactional entity")
}
mpt.config.driver = _tx.drv
return mpt
}
// String implements the fmt.Stringer.
func (mpt *MediaProcessTask) String() string {
var builder strings.Builder
builder.WriteString("MediaProcessTask(")
builder.WriteString(fmt.Sprintf("id=%v, ", mpt.ID))
builder.WriteString("created_at=")
builder.WriteString(mpt.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(mpt.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
if v := mpt.DeletedAt; v != nil {
builder.WriteString("deleted_at=")
builder.WriteString(v.Format(time.ANSIC))
}
builder.WriteString(", ")
builder.WriteString("media_type=")
builder.WriteString(fmt.Sprintf("%v", mpt.MediaType))
builder.WriteString(", ")
builder.WriteString("status=")
builder.WriteString(fmt.Sprintf("%v", mpt.Status))
builder.WriteString(", ")
builder.WriteString("entity_id=")
builder.WriteString(fmt.Sprintf("%v", mpt.EntityID))
builder.WriteString(", ")
builder.WriteString("file_id=")
builder.WriteString(fmt.Sprintf("%v", mpt.FileID))
builder.WriteString(", ")
builder.WriteString("owner_id=")
builder.WriteString(fmt.Sprintf("%v", mpt.OwnerID))
builder.WriteString(", ")
builder.WriteString("attempts=")
builder.WriteString(fmt.Sprintf("%v", mpt.Attempts))
builder.WriteString(", ")
builder.WriteString("error=")
builder.WriteString(mpt.Error)
builder.WriteString(", ")
builder.WriteString("result_size=")
builder.WriteString(fmt.Sprintf("%v", mpt.ResultSize))
builder.WriteString(", ")
builder.WriteString("props=")
builder.WriteString(fmt.Sprintf("%v", mpt.Props))
builder.WriteByte(')')
return builder.String()
}
// MediaProcessTasks is a parsable slice of MediaProcessTask.
type MediaProcessTasks []*MediaProcessTask

@ -0,0 +1,207 @@
// Code generated by ent, DO NOT EDIT.
package mediaprocesstask
import (
"fmt"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
)
const (
// Label holds the string label denoting the mediaprocesstask type in the database.
Label = "media_process_task"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at"
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
FieldUpdatedAt = "updated_at"
// FieldDeletedAt holds the string denoting the deleted_at field in the database.
FieldDeletedAt = "deleted_at"
// FieldMediaType holds the string denoting the media_type field in the database.
FieldMediaType = "media_type"
// FieldStatus holds the string denoting the status field in the database.
FieldStatus = "status"
// FieldEntityID holds the string denoting the entity_id field in the database.
FieldEntityID = "entity_id"
// FieldFileID holds the string denoting the file_id field in the database.
FieldFileID = "file_id"
// FieldOwnerID holds the string denoting the owner_id field in the database.
FieldOwnerID = "owner_id"
// FieldAttempts holds the string denoting the attempts field in the database.
FieldAttempts = "attempts"
// FieldError holds the string denoting the error field in the database.
FieldError = "error"
// FieldResultSize holds the string denoting the result_size field in the database.
FieldResultSize = "result_size"
// FieldProps holds the string denoting the props field in the database.
FieldProps = "props"
// Table holds the table name of the mediaprocesstask in the database.
Table = "media_process_tasks"
)
// Columns holds all SQL columns for mediaprocesstask fields.
var Columns = []string{
FieldID,
FieldCreatedAt,
FieldUpdatedAt,
FieldDeletedAt,
FieldMediaType,
FieldStatus,
FieldEntityID,
FieldFileID,
FieldOwnerID,
FieldAttempts,
FieldError,
FieldResultSize,
FieldProps,
}
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
if column == Columns[i] {
return true
}
}
return false
}
// Note that the variables below are initialized by the runtime
// package on the initialization of the application. Therefore,
// it should be imported in the main as follows:
//
// import _ "github.com/cloudreve/Cloudreve/v4/ent/runtime"
var (
Hooks [1]ent.Hook
Interceptors [1]ent.Interceptor
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultAttempts holds the default value on creation for the "attempts" field.
DefaultAttempts int
)
// MediaType defines the type for the "media_type" enum field.
type MediaType string
// MediaTypeImage is the default value of the MediaType enum.
const DefaultMediaType = MediaTypeImage
// MediaType values.
const (
MediaTypeImage MediaType = "image"
MediaTypeVideo MediaType = "video"
)
func (mt MediaType) String() string {
return string(mt)
}
// MediaTypeValidator is a validator for the "media_type" field enum values. It is called by the builders before save.
func MediaTypeValidator(mt MediaType) error {
switch mt {
case MediaTypeImage, MediaTypeVideo:
return nil
default:
return fmt.Errorf("mediaprocesstask: invalid enum value for media_type field: %q", mt)
}
}
// Status defines the type for the "status" enum field.
type Status string
// StatusPending is the default value of the Status enum.
const DefaultStatus = StatusPending
// Status values.
const (
StatusPending Status = "pending"
StatusProcessing Status = "processing"
StatusDone Status = "done"
StatusFailed Status = "failed"
StatusSkipped Status = "skipped"
)
func (s Status) String() string {
return string(s)
}
// StatusValidator is a validator for the "status" field enum values. It is called by the builders before save.
func StatusValidator(s Status) error {
switch s {
case StatusPending, StatusProcessing, StatusDone, StatusFailed, StatusSkipped:
return nil
default:
return fmt.Errorf("mediaprocesstask: invalid enum value for status field: %q", s)
}
}
// OrderOption defines the ordering options for the MediaProcessTask queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByDeletedAt orders the results by the deleted_at field.
func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
}
// ByMediaType orders the results by the media_type field.
func ByMediaType(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldMediaType, opts...).ToFunc()
}
// ByStatus orders the results by the status field.
func ByStatus(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldStatus, opts...).ToFunc()
}
// ByEntityID orders the results by the entity_id field.
func ByEntityID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldEntityID, opts...).ToFunc()
}
// ByFileID orders the results by the file_id field.
func ByFileID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldFileID, opts...).ToFunc()
}
// ByOwnerID orders the results by the owner_id field.
func ByOwnerID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldOwnerID, opts...).ToFunc()
}
// ByAttempts orders the results by the attempts field.
func ByAttempts(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldAttempts, opts...).ToFunc()
}
// ByError orders the results by the error field.
func ByError(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldError, opts...).ToFunc()
}
// ByResultSize orders the results by the result_size field.
func ByResultSize(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldResultSize, opts...).ToFunc()
}

@ -0,0 +1,590 @@
// Code generated by ent, DO NOT EDIT.
package mediaprocesstask
import (
"time"
"entgo.io/ent/dialect/sql"
"github.com/cloudreve/Cloudreve/v4/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldID, id))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldUpdatedAt, v))
}
// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
func DeletedAt(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldDeletedAt, v))
}
// EntityID applies equality check predicate on the "entity_id" field. It's identical to EntityIDEQ.
func EntityID(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldEntityID, v))
}
// FileID applies equality check predicate on the "file_id" field. It's identical to FileIDEQ.
func FileID(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldFileID, v))
}
// OwnerID applies equality check predicate on the "owner_id" field. It's identical to OwnerIDEQ.
func OwnerID(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldOwnerID, v))
}
// Attempts applies equality check predicate on the "attempts" field. It's identical to AttemptsEQ.
func Attempts(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldAttempts, v))
}
// Error applies equality check predicate on the "error" field. It's identical to ErrorEQ.
func Error(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldError, v))
}
// ResultSize applies equality check predicate on the "result_size" field. It's identical to ResultSizeEQ.
func ResultSize(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldResultSize, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldUpdatedAt, v))
}
// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
func DeletedAtEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldDeletedAt, v))
}
// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
func DeletedAtNEQ(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldDeletedAt, v))
}
// DeletedAtIn applies the In predicate on the "deleted_at" field.
func DeletedAtIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldDeletedAt, vs...))
}
// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
func DeletedAtNotIn(vs ...time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldDeletedAt, vs...))
}
// DeletedAtGT applies the GT predicate on the "deleted_at" field.
func DeletedAtGT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldDeletedAt, v))
}
// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
func DeletedAtGTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldDeletedAt, v))
}
// DeletedAtLT applies the LT predicate on the "deleted_at" field.
func DeletedAtLT(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldDeletedAt, v))
}
// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
func DeletedAtLTE(v time.Time) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldDeletedAt, v))
}
// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
func DeletedAtIsNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIsNull(FieldDeletedAt))
}
// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
func DeletedAtNotNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotNull(FieldDeletedAt))
}
// MediaTypeEQ applies the EQ predicate on the "media_type" field.
func MediaTypeEQ(v MediaType) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldMediaType, v))
}
// MediaTypeNEQ applies the NEQ predicate on the "media_type" field.
func MediaTypeNEQ(v MediaType) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldMediaType, v))
}
// MediaTypeIn applies the In predicate on the "media_type" field.
func MediaTypeIn(vs ...MediaType) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldMediaType, vs...))
}
// MediaTypeNotIn applies the NotIn predicate on the "media_type" field.
func MediaTypeNotIn(vs ...MediaType) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldMediaType, vs...))
}
// StatusEQ applies the EQ predicate on the "status" field.
func StatusEQ(v Status) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldStatus, v))
}
// StatusNEQ applies the NEQ predicate on the "status" field.
func StatusNEQ(v Status) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldStatus, v))
}
// StatusIn applies the In predicate on the "status" field.
func StatusIn(vs ...Status) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldStatus, vs...))
}
// StatusNotIn applies the NotIn predicate on the "status" field.
func StatusNotIn(vs ...Status) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldStatus, vs...))
}
// EntityIDEQ applies the EQ predicate on the "entity_id" field.
func EntityIDEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldEntityID, v))
}
// EntityIDNEQ applies the NEQ predicate on the "entity_id" field.
func EntityIDNEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldEntityID, v))
}
// EntityIDIn applies the In predicate on the "entity_id" field.
func EntityIDIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldEntityID, vs...))
}
// EntityIDNotIn applies the NotIn predicate on the "entity_id" field.
func EntityIDNotIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldEntityID, vs...))
}
// EntityIDGT applies the GT predicate on the "entity_id" field.
func EntityIDGT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldEntityID, v))
}
// EntityIDGTE applies the GTE predicate on the "entity_id" field.
func EntityIDGTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldEntityID, v))
}
// EntityIDLT applies the LT predicate on the "entity_id" field.
func EntityIDLT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldEntityID, v))
}
// EntityIDLTE applies the LTE predicate on the "entity_id" field.
func EntityIDLTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldEntityID, v))
}
// FileIDEQ applies the EQ predicate on the "file_id" field.
func FileIDEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldFileID, v))
}
// FileIDNEQ applies the NEQ predicate on the "file_id" field.
func FileIDNEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldFileID, v))
}
// FileIDIn applies the In predicate on the "file_id" field.
func FileIDIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldFileID, vs...))
}
// FileIDNotIn applies the NotIn predicate on the "file_id" field.
func FileIDNotIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldFileID, vs...))
}
// FileIDGT applies the GT predicate on the "file_id" field.
func FileIDGT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldFileID, v))
}
// FileIDGTE applies the GTE predicate on the "file_id" field.
func FileIDGTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldFileID, v))
}
// FileIDLT applies the LT predicate on the "file_id" field.
func FileIDLT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldFileID, v))
}
// FileIDLTE applies the LTE predicate on the "file_id" field.
func FileIDLTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldFileID, v))
}
// FileIDIsNil applies the IsNil predicate on the "file_id" field.
func FileIDIsNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIsNull(FieldFileID))
}
// FileIDNotNil applies the NotNil predicate on the "file_id" field.
func FileIDNotNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotNull(FieldFileID))
}
// OwnerIDEQ applies the EQ predicate on the "owner_id" field.
func OwnerIDEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldOwnerID, v))
}
// OwnerIDNEQ applies the NEQ predicate on the "owner_id" field.
func OwnerIDNEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldOwnerID, v))
}
// OwnerIDIn applies the In predicate on the "owner_id" field.
func OwnerIDIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldOwnerID, vs...))
}
// OwnerIDNotIn applies the NotIn predicate on the "owner_id" field.
func OwnerIDNotIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldOwnerID, vs...))
}
// OwnerIDGT applies the GT predicate on the "owner_id" field.
func OwnerIDGT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldOwnerID, v))
}
// OwnerIDGTE applies the GTE predicate on the "owner_id" field.
func OwnerIDGTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldOwnerID, v))
}
// OwnerIDLT applies the LT predicate on the "owner_id" field.
func OwnerIDLT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldOwnerID, v))
}
// OwnerIDLTE applies the LTE predicate on the "owner_id" field.
func OwnerIDLTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldOwnerID, v))
}
// AttemptsEQ applies the EQ predicate on the "attempts" field.
func AttemptsEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldAttempts, v))
}
// AttemptsNEQ applies the NEQ predicate on the "attempts" field.
func AttemptsNEQ(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldAttempts, v))
}
// AttemptsIn applies the In predicate on the "attempts" field.
func AttemptsIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldAttempts, vs...))
}
// AttemptsNotIn applies the NotIn predicate on the "attempts" field.
func AttemptsNotIn(vs ...int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldAttempts, vs...))
}
// AttemptsGT applies the GT predicate on the "attempts" field.
func AttemptsGT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldAttempts, v))
}
// AttemptsGTE applies the GTE predicate on the "attempts" field.
func AttemptsGTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldAttempts, v))
}
// AttemptsLT applies the LT predicate on the "attempts" field.
func AttemptsLT(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldAttempts, v))
}
// AttemptsLTE applies the LTE predicate on the "attempts" field.
func AttemptsLTE(v int) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldAttempts, v))
}
// ErrorEQ applies the EQ predicate on the "error" field.
func ErrorEQ(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldError, v))
}
// ErrorNEQ applies the NEQ predicate on the "error" field.
func ErrorNEQ(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldError, v))
}
// ErrorIn applies the In predicate on the "error" field.
func ErrorIn(vs ...string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldError, vs...))
}
// ErrorNotIn applies the NotIn predicate on the "error" field.
func ErrorNotIn(vs ...string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldError, vs...))
}
// ErrorGT applies the GT predicate on the "error" field.
func ErrorGT(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldError, v))
}
// ErrorGTE applies the GTE predicate on the "error" field.
func ErrorGTE(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldError, v))
}
// ErrorLT applies the LT predicate on the "error" field.
func ErrorLT(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldError, v))
}
// ErrorLTE applies the LTE predicate on the "error" field.
func ErrorLTE(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldError, v))
}
// ErrorContains applies the Contains predicate on the "error" field.
func ErrorContains(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldContains(FieldError, v))
}
// ErrorHasPrefix applies the HasPrefix predicate on the "error" field.
func ErrorHasPrefix(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldHasPrefix(FieldError, v))
}
// ErrorHasSuffix applies the HasSuffix predicate on the "error" field.
func ErrorHasSuffix(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldHasSuffix(FieldError, v))
}
// ErrorIsNil applies the IsNil predicate on the "error" field.
func ErrorIsNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIsNull(FieldError))
}
// ErrorNotNil applies the NotNil predicate on the "error" field.
func ErrorNotNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotNull(FieldError))
}
// ErrorEqualFold applies the EqualFold predicate on the "error" field.
func ErrorEqualFold(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEqualFold(FieldError, v))
}
// ErrorContainsFold applies the ContainsFold predicate on the "error" field.
func ErrorContainsFold(v string) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldContainsFold(FieldError, v))
}
// ResultSizeEQ applies the EQ predicate on the "result_size" field.
func ResultSizeEQ(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldEQ(FieldResultSize, v))
}
// ResultSizeNEQ applies the NEQ predicate on the "result_size" field.
func ResultSizeNEQ(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNEQ(FieldResultSize, v))
}
// ResultSizeIn applies the In predicate on the "result_size" field.
func ResultSizeIn(vs ...int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIn(FieldResultSize, vs...))
}
// ResultSizeNotIn applies the NotIn predicate on the "result_size" field.
func ResultSizeNotIn(vs ...int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotIn(FieldResultSize, vs...))
}
// ResultSizeGT applies the GT predicate on the "result_size" field.
func ResultSizeGT(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGT(FieldResultSize, v))
}
// ResultSizeGTE applies the GTE predicate on the "result_size" field.
func ResultSizeGTE(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldGTE(FieldResultSize, v))
}
// ResultSizeLT applies the LT predicate on the "result_size" field.
func ResultSizeLT(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLT(FieldResultSize, v))
}
// ResultSizeLTE applies the LTE predicate on the "result_size" field.
func ResultSizeLTE(v int64) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldLTE(FieldResultSize, v))
}
// ResultSizeIsNil applies the IsNil predicate on the "result_size" field.
func ResultSizeIsNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIsNull(FieldResultSize))
}
// ResultSizeNotNil applies the NotNil predicate on the "result_size" field.
func ResultSizeNotNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotNull(FieldResultSize))
}
// PropsIsNil applies the IsNil predicate on the "props" field.
func PropsIsNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldIsNull(FieldProps))
}
// PropsNotNil applies the NotNil predicate on the "props" field.
func PropsNotNil() predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.FieldNotNull(FieldProps))
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.MediaProcessTask) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.MediaProcessTask) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.MediaProcessTask) predicate.MediaProcessTask {
return predicate.MediaProcessTask(sql.NotPredicates(p))
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/predicate"
)
// MediaProcessTaskDelete is the builder for deleting a MediaProcessTask entity.
type MediaProcessTaskDelete struct {
config
hooks []Hook
mutation *MediaProcessTaskMutation
}
// Where appends a list predicates to the MediaProcessTaskDelete builder.
func (mptd *MediaProcessTaskDelete) Where(ps ...predicate.MediaProcessTask) *MediaProcessTaskDelete {
mptd.mutation.Where(ps...)
return mptd
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (mptd *MediaProcessTaskDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, mptd.sqlExec, mptd.mutation, mptd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (mptd *MediaProcessTaskDelete) ExecX(ctx context.Context) int {
n, err := mptd.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (mptd *MediaProcessTaskDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(mediaprocesstask.Table, sqlgraph.NewFieldSpec(mediaprocesstask.FieldID, field.TypeInt))
if ps := mptd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, mptd.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
mptd.mutation.done = true
return affected, err
}
// MediaProcessTaskDeleteOne is the builder for deleting a single MediaProcessTask entity.
type MediaProcessTaskDeleteOne struct {
mptd *MediaProcessTaskDelete
}
// Where appends a list predicates to the MediaProcessTaskDelete builder.
func (mptdo *MediaProcessTaskDeleteOne) Where(ps ...predicate.MediaProcessTask) *MediaProcessTaskDeleteOne {
mptdo.mptd.mutation.Where(ps...)
return mptdo
}
// Exec executes the deletion query.
func (mptdo *MediaProcessTaskDeleteOne) Exec(ctx context.Context) error {
n, err := mptdo.mptd.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{mediaprocesstask.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (mptdo *MediaProcessTaskDeleteOne) ExecX(ctx context.Context) {
if err := mptdo.Exec(ctx); err != nil {
panic(err)
}
}

@ -0,0 +1,526 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"math"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/predicate"
)
// MediaProcessTaskQuery is the builder for querying MediaProcessTask entities.
type MediaProcessTaskQuery struct {
config
ctx *QueryContext
order []mediaprocesstask.OrderOption
inters []Interceptor
predicates []predicate.MediaProcessTask
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the MediaProcessTaskQuery builder.
func (mptq *MediaProcessTaskQuery) Where(ps ...predicate.MediaProcessTask) *MediaProcessTaskQuery {
mptq.predicates = append(mptq.predicates, ps...)
return mptq
}
// Limit the number of records to be returned by this query.
func (mptq *MediaProcessTaskQuery) Limit(limit int) *MediaProcessTaskQuery {
mptq.ctx.Limit = &limit
return mptq
}
// Offset to start from.
func (mptq *MediaProcessTaskQuery) Offset(offset int) *MediaProcessTaskQuery {
mptq.ctx.Offset = &offset
return mptq
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (mptq *MediaProcessTaskQuery) Unique(unique bool) *MediaProcessTaskQuery {
mptq.ctx.Unique = &unique
return mptq
}
// Order specifies how the records should be ordered.
func (mptq *MediaProcessTaskQuery) Order(o ...mediaprocesstask.OrderOption) *MediaProcessTaskQuery {
mptq.order = append(mptq.order, o...)
return mptq
}
// First returns the first MediaProcessTask entity from the query.
// Returns a *NotFoundError when no MediaProcessTask was found.
func (mptq *MediaProcessTaskQuery) First(ctx context.Context) (*MediaProcessTask, error) {
nodes, err := mptq.Limit(1).All(setContextOp(ctx, mptq.ctx, "First"))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{mediaprocesstask.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) FirstX(ctx context.Context) *MediaProcessTask {
node, err := mptq.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first MediaProcessTask ID from the query.
// Returns a *NotFoundError when no MediaProcessTask ID was found.
func (mptq *MediaProcessTaskQuery) FirstID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = mptq.Limit(1).IDs(setContextOp(ctx, mptq.ctx, "FirstID")); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{mediaprocesstask.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) FirstIDX(ctx context.Context) int {
id, err := mptq.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single MediaProcessTask entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one MediaProcessTask entity is found.
// Returns a *NotFoundError when no MediaProcessTask entities are found.
func (mptq *MediaProcessTaskQuery) Only(ctx context.Context) (*MediaProcessTask, error) {
nodes, err := mptq.Limit(2).All(setContextOp(ctx, mptq.ctx, "Only"))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{mediaprocesstask.Label}
default:
return nil, &NotSingularError{mediaprocesstask.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) OnlyX(ctx context.Context) *MediaProcessTask {
node, err := mptq.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only MediaProcessTask ID in the query.
// Returns a *NotSingularError when more than one MediaProcessTask ID is found.
// Returns a *NotFoundError when no entities are found.
func (mptq *MediaProcessTaskQuery) OnlyID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = mptq.Limit(2).IDs(setContextOp(ctx, mptq.ctx, "OnlyID")); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{mediaprocesstask.Label}
default:
err = &NotSingularError{mediaprocesstask.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) OnlyIDX(ctx context.Context) int {
id, err := mptq.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of MediaProcessTasks.
func (mptq *MediaProcessTaskQuery) All(ctx context.Context) ([]*MediaProcessTask, error) {
ctx = setContextOp(ctx, mptq.ctx, "All")
if err := mptq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*MediaProcessTask, *MediaProcessTaskQuery]()
return withInterceptors[[]*MediaProcessTask](ctx, mptq, qr, mptq.inters)
}
// AllX is like All, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) AllX(ctx context.Context) []*MediaProcessTask {
nodes, err := mptq.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of MediaProcessTask IDs.
func (mptq *MediaProcessTaskQuery) IDs(ctx context.Context) (ids []int, err error) {
if mptq.ctx.Unique == nil && mptq.path != nil {
mptq.Unique(true)
}
ctx = setContextOp(ctx, mptq.ctx, "IDs")
if err = mptq.Select(mediaprocesstask.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) IDsX(ctx context.Context) []int {
ids, err := mptq.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (mptq *MediaProcessTaskQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, mptq.ctx, "Count")
if err := mptq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, mptq, querierCount[*MediaProcessTaskQuery](), mptq.inters)
}
// CountX is like Count, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) CountX(ctx context.Context) int {
count, err := mptq.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (mptq *MediaProcessTaskQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, mptq.ctx, "Exist")
switch _, err := mptq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (mptq *MediaProcessTaskQuery) ExistX(ctx context.Context) bool {
exist, err := mptq.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the MediaProcessTaskQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (mptq *MediaProcessTaskQuery) Clone() *MediaProcessTaskQuery {
if mptq == nil {
return nil
}
return &MediaProcessTaskQuery{
config: mptq.config,
ctx: mptq.ctx.Clone(),
order: append([]mediaprocesstask.OrderOption{}, mptq.order...),
inters: append([]Interceptor{}, mptq.inters...),
predicates: append([]predicate.MediaProcessTask{}, mptq.predicates...),
// clone intermediate query.
sql: mptq.sql.Clone(),
path: mptq.path,
}
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.MediaProcessTask.Query().
// GroupBy(mediaprocesstask.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (mptq *MediaProcessTaskQuery) GroupBy(field string, fields ...string) *MediaProcessTaskGroupBy {
mptq.ctx.Fields = append([]string{field}, fields...)
grbuild := &MediaProcessTaskGroupBy{build: mptq}
grbuild.flds = &mptq.ctx.Fields
grbuild.label = mediaprocesstask.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// }
//
// client.MediaProcessTask.Query().
// Select(mediaprocesstask.FieldCreatedAt).
// Scan(ctx, &v)
func (mptq *MediaProcessTaskQuery) Select(fields ...string) *MediaProcessTaskSelect {
mptq.ctx.Fields = append(mptq.ctx.Fields, fields...)
sbuild := &MediaProcessTaskSelect{MediaProcessTaskQuery: mptq}
sbuild.label = mediaprocesstask.Label
sbuild.flds, sbuild.scan = &mptq.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a MediaProcessTaskSelect configured with the given aggregations.
func (mptq *MediaProcessTaskQuery) Aggregate(fns ...AggregateFunc) *MediaProcessTaskSelect {
return mptq.Select().Aggregate(fns...)
}
func (mptq *MediaProcessTaskQuery) prepareQuery(ctx context.Context) error {
for _, inter := range mptq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, mptq); err != nil {
return err
}
}
}
for _, f := range mptq.ctx.Fields {
if !mediaprocesstask.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if mptq.path != nil {
prev, err := mptq.path(ctx)
if err != nil {
return err
}
mptq.sql = prev
}
return nil
}
func (mptq *MediaProcessTaskQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*MediaProcessTask, error) {
var (
nodes = []*MediaProcessTask{}
_spec = mptq.querySpec()
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*MediaProcessTask).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &MediaProcessTask{config: mptq.config}
nodes = append(nodes, node)
return node.assignValues(columns, values)
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, mptq.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
return nodes, nil
}
func (mptq *MediaProcessTaskQuery) sqlCount(ctx context.Context) (int, error) {
_spec := mptq.querySpec()
_spec.Node.Columns = mptq.ctx.Fields
if len(mptq.ctx.Fields) > 0 {
_spec.Unique = mptq.ctx.Unique != nil && *mptq.ctx.Unique
}
return sqlgraph.CountNodes(ctx, mptq.driver, _spec)
}
func (mptq *MediaProcessTaskQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(mediaprocesstask.Table, mediaprocesstask.Columns, sqlgraph.NewFieldSpec(mediaprocesstask.FieldID, field.TypeInt))
_spec.From = mptq.sql
if unique := mptq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if mptq.path != nil {
_spec.Unique = true
}
if fields := mptq.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, mediaprocesstask.FieldID)
for i := range fields {
if fields[i] != mediaprocesstask.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
}
if ps := mptq.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := mptq.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := mptq.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := mptq.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (mptq *MediaProcessTaskQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(mptq.driver.Dialect())
t1 := builder.Table(mediaprocesstask.Table)
columns := mptq.ctx.Fields
if len(columns) == 0 {
columns = mediaprocesstask.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if mptq.sql != nil {
selector = mptq.sql
selector.Select(selector.Columns(columns...)...)
}
if mptq.ctx.Unique != nil && *mptq.ctx.Unique {
selector.Distinct()
}
for _, p := range mptq.predicates {
p(selector)
}
for _, p := range mptq.order {
p(selector)
}
if offset := mptq.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := mptq.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// MediaProcessTaskGroupBy is the group-by builder for MediaProcessTask entities.
type MediaProcessTaskGroupBy struct {
selector
build *MediaProcessTaskQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (mptgb *MediaProcessTaskGroupBy) Aggregate(fns ...AggregateFunc) *MediaProcessTaskGroupBy {
mptgb.fns = append(mptgb.fns, fns...)
return mptgb
}
// Scan applies the selector query and scans the result into the given value.
func (mptgb *MediaProcessTaskGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, mptgb.build.ctx, "GroupBy")
if err := mptgb.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*MediaProcessTaskQuery, *MediaProcessTaskGroupBy](ctx, mptgb.build, mptgb, mptgb.build.inters, v)
}
func (mptgb *MediaProcessTaskGroupBy) sqlScan(ctx context.Context, root *MediaProcessTaskQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(mptgb.fns))
for _, fn := range mptgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*mptgb.flds)+len(mptgb.fns))
for _, f := range *mptgb.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*mptgb.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := mptgb.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// MediaProcessTaskSelect is the builder for selecting fields of MediaProcessTask entities.
type MediaProcessTaskSelect struct {
*MediaProcessTaskQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (mpts *MediaProcessTaskSelect) Aggregate(fns ...AggregateFunc) *MediaProcessTaskSelect {
mpts.fns = append(mpts.fns, fns...)
return mpts
}
// Scan applies the selector query and scans the result into the given value.
func (mpts *MediaProcessTaskSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, mpts.ctx, "Select")
if err := mpts.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*MediaProcessTaskQuery, *MediaProcessTaskSelect](ctx, mpts.MediaProcessTaskQuery, mpts, mpts.inters, v)
}
func (mpts *MediaProcessTaskSelect) sqlScan(ctx context.Context, root *MediaProcessTaskQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(mpts.fns))
for _, fn := range mpts.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*mpts.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := mpts.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

@ -0,0 +1,775 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/predicate"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// MediaProcessTaskUpdate is the builder for updating MediaProcessTask entities.
type MediaProcessTaskUpdate struct {
config
hooks []Hook
mutation *MediaProcessTaskMutation
}
// Where appends a list predicates to the MediaProcessTaskUpdate builder.
func (mptu *MediaProcessTaskUpdate) Where(ps ...predicate.MediaProcessTask) *MediaProcessTaskUpdate {
mptu.mutation.Where(ps...)
return mptu
}
// SetUpdatedAt sets the "updated_at" field.
func (mptu *MediaProcessTaskUpdate) SetUpdatedAt(t time.Time) *MediaProcessTaskUpdate {
mptu.mutation.SetUpdatedAt(t)
return mptu
}
// SetDeletedAt sets the "deleted_at" field.
func (mptu *MediaProcessTaskUpdate) SetDeletedAt(t time.Time) *MediaProcessTaskUpdate {
mptu.mutation.SetDeletedAt(t)
return mptu
}
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableDeletedAt(t *time.Time) *MediaProcessTaskUpdate {
if t != nil {
mptu.SetDeletedAt(*t)
}
return mptu
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (mptu *MediaProcessTaskUpdate) ClearDeletedAt() *MediaProcessTaskUpdate {
mptu.mutation.ClearDeletedAt()
return mptu
}
// SetMediaType sets the "media_type" field.
func (mptu *MediaProcessTaskUpdate) SetMediaType(mt mediaprocesstask.MediaType) *MediaProcessTaskUpdate {
mptu.mutation.SetMediaType(mt)
return mptu
}
// SetNillableMediaType sets the "media_type" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableMediaType(mt *mediaprocesstask.MediaType) *MediaProcessTaskUpdate {
if mt != nil {
mptu.SetMediaType(*mt)
}
return mptu
}
// SetStatus sets the "status" field.
func (mptu *MediaProcessTaskUpdate) SetStatus(m mediaprocesstask.Status) *MediaProcessTaskUpdate {
mptu.mutation.SetStatus(m)
return mptu
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableStatus(m *mediaprocesstask.Status) *MediaProcessTaskUpdate {
if m != nil {
mptu.SetStatus(*m)
}
return mptu
}
// SetEntityID sets the "entity_id" field.
func (mptu *MediaProcessTaskUpdate) SetEntityID(i int) *MediaProcessTaskUpdate {
mptu.mutation.ResetEntityID()
mptu.mutation.SetEntityID(i)
return mptu
}
// SetNillableEntityID sets the "entity_id" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableEntityID(i *int) *MediaProcessTaskUpdate {
if i != nil {
mptu.SetEntityID(*i)
}
return mptu
}
// AddEntityID adds i to the "entity_id" field.
func (mptu *MediaProcessTaskUpdate) AddEntityID(i int) *MediaProcessTaskUpdate {
mptu.mutation.AddEntityID(i)
return mptu
}
// SetFileID sets the "file_id" field.
func (mptu *MediaProcessTaskUpdate) SetFileID(i int) *MediaProcessTaskUpdate {
mptu.mutation.ResetFileID()
mptu.mutation.SetFileID(i)
return mptu
}
// SetNillableFileID sets the "file_id" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableFileID(i *int) *MediaProcessTaskUpdate {
if i != nil {
mptu.SetFileID(*i)
}
return mptu
}
// AddFileID adds i to the "file_id" field.
func (mptu *MediaProcessTaskUpdate) AddFileID(i int) *MediaProcessTaskUpdate {
mptu.mutation.AddFileID(i)
return mptu
}
// ClearFileID clears the value of the "file_id" field.
func (mptu *MediaProcessTaskUpdate) ClearFileID() *MediaProcessTaskUpdate {
mptu.mutation.ClearFileID()
return mptu
}
// SetOwnerID sets the "owner_id" field.
func (mptu *MediaProcessTaskUpdate) SetOwnerID(i int) *MediaProcessTaskUpdate {
mptu.mutation.ResetOwnerID()
mptu.mutation.SetOwnerID(i)
return mptu
}
// SetNillableOwnerID sets the "owner_id" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableOwnerID(i *int) *MediaProcessTaskUpdate {
if i != nil {
mptu.SetOwnerID(*i)
}
return mptu
}
// AddOwnerID adds i to the "owner_id" field.
func (mptu *MediaProcessTaskUpdate) AddOwnerID(i int) *MediaProcessTaskUpdate {
mptu.mutation.AddOwnerID(i)
return mptu
}
// SetAttempts sets the "attempts" field.
func (mptu *MediaProcessTaskUpdate) SetAttempts(i int) *MediaProcessTaskUpdate {
mptu.mutation.ResetAttempts()
mptu.mutation.SetAttempts(i)
return mptu
}
// SetNillableAttempts sets the "attempts" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableAttempts(i *int) *MediaProcessTaskUpdate {
if i != nil {
mptu.SetAttempts(*i)
}
return mptu
}
// AddAttempts adds i to the "attempts" field.
func (mptu *MediaProcessTaskUpdate) AddAttempts(i int) *MediaProcessTaskUpdate {
mptu.mutation.AddAttempts(i)
return mptu
}
// SetError sets the "error" field.
func (mptu *MediaProcessTaskUpdate) SetError(s string) *MediaProcessTaskUpdate {
mptu.mutation.SetError(s)
return mptu
}
// SetNillableError sets the "error" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableError(s *string) *MediaProcessTaskUpdate {
if s != nil {
mptu.SetError(*s)
}
return mptu
}
// ClearError clears the value of the "error" field.
func (mptu *MediaProcessTaskUpdate) ClearError() *MediaProcessTaskUpdate {
mptu.mutation.ClearError()
return mptu
}
// SetResultSize sets the "result_size" field.
func (mptu *MediaProcessTaskUpdate) SetResultSize(i int64) *MediaProcessTaskUpdate {
mptu.mutation.ResetResultSize()
mptu.mutation.SetResultSize(i)
return mptu
}
// SetNillableResultSize sets the "result_size" field if the given value is not nil.
func (mptu *MediaProcessTaskUpdate) SetNillableResultSize(i *int64) *MediaProcessTaskUpdate {
if i != nil {
mptu.SetResultSize(*i)
}
return mptu
}
// AddResultSize adds i to the "result_size" field.
func (mptu *MediaProcessTaskUpdate) AddResultSize(i int64) *MediaProcessTaskUpdate {
mptu.mutation.AddResultSize(i)
return mptu
}
// ClearResultSize clears the value of the "result_size" field.
func (mptu *MediaProcessTaskUpdate) ClearResultSize() *MediaProcessTaskUpdate {
mptu.mutation.ClearResultSize()
return mptu
}
// SetProps sets the "props" field.
func (mptu *MediaProcessTaskUpdate) SetProps(tptp *types.MediaProcessTaskProps) *MediaProcessTaskUpdate {
mptu.mutation.SetProps(tptp)
return mptu
}
// ClearProps clears the value of the "props" field.
func (mptu *MediaProcessTaskUpdate) ClearProps() *MediaProcessTaskUpdate {
mptu.mutation.ClearProps()
return mptu
}
// Mutation returns the MediaProcessTaskMutation object of the builder.
func (mptu *MediaProcessTaskUpdate) Mutation() *MediaProcessTaskMutation {
return mptu.mutation
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (mptu *MediaProcessTaskUpdate) Save(ctx context.Context) (int, error) {
if err := mptu.defaults(); err != nil {
return 0, err
}
return withHooks(ctx, mptu.sqlSave, mptu.mutation, mptu.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (mptu *MediaProcessTaskUpdate) SaveX(ctx context.Context) int {
affected, err := mptu.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (mptu *MediaProcessTaskUpdate) Exec(ctx context.Context) error {
_, err := mptu.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (mptu *MediaProcessTaskUpdate) ExecX(ctx context.Context) {
if err := mptu.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (mptu *MediaProcessTaskUpdate) defaults() error {
if _, ok := mptu.mutation.UpdatedAt(); !ok {
if mediaprocesstask.UpdateDefaultUpdatedAt == nil {
return fmt.Errorf("ent: uninitialized mediaprocesstask.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
}
v := mediaprocesstask.UpdateDefaultUpdatedAt()
mptu.mutation.SetUpdatedAt(v)
}
return nil
}
// check runs all checks and user-defined validators on the builder.
func (mptu *MediaProcessTaskUpdate) check() error {
if v, ok := mptu.mutation.MediaType(); ok {
if err := mediaprocesstask.MediaTypeValidator(v); err != nil {
return &ValidationError{Name: "media_type", err: fmt.Errorf(`ent: validator failed for field "MediaProcessTask.media_type": %w`, err)}
}
}
if v, ok := mptu.mutation.Status(); ok {
if err := mediaprocesstask.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "MediaProcessTask.status": %w`, err)}
}
}
return nil
}
func (mptu *MediaProcessTaskUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := mptu.check(); err != nil {
return n, err
}
_spec := sqlgraph.NewUpdateSpec(mediaprocesstask.Table, mediaprocesstask.Columns, sqlgraph.NewFieldSpec(mediaprocesstask.FieldID, field.TypeInt))
if ps := mptu.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := mptu.mutation.UpdatedAt(); ok {
_spec.SetField(mediaprocesstask.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := mptu.mutation.DeletedAt(); ok {
_spec.SetField(mediaprocesstask.FieldDeletedAt, field.TypeTime, value)
}
if mptu.mutation.DeletedAtCleared() {
_spec.ClearField(mediaprocesstask.FieldDeletedAt, field.TypeTime)
}
if value, ok := mptu.mutation.MediaType(); ok {
_spec.SetField(mediaprocesstask.FieldMediaType, field.TypeEnum, value)
}
if value, ok := mptu.mutation.Status(); ok {
_spec.SetField(mediaprocesstask.FieldStatus, field.TypeEnum, value)
}
if value, ok := mptu.mutation.EntityID(); ok {
_spec.SetField(mediaprocesstask.FieldEntityID, field.TypeInt, value)
}
if value, ok := mptu.mutation.AddedEntityID(); ok {
_spec.AddField(mediaprocesstask.FieldEntityID, field.TypeInt, value)
}
if value, ok := mptu.mutation.FileID(); ok {
_spec.SetField(mediaprocesstask.FieldFileID, field.TypeInt, value)
}
if value, ok := mptu.mutation.AddedFileID(); ok {
_spec.AddField(mediaprocesstask.FieldFileID, field.TypeInt, value)
}
if mptu.mutation.FileIDCleared() {
_spec.ClearField(mediaprocesstask.FieldFileID, field.TypeInt)
}
if value, ok := mptu.mutation.OwnerID(); ok {
_spec.SetField(mediaprocesstask.FieldOwnerID, field.TypeInt, value)
}
if value, ok := mptu.mutation.AddedOwnerID(); ok {
_spec.AddField(mediaprocesstask.FieldOwnerID, field.TypeInt, value)
}
if value, ok := mptu.mutation.Attempts(); ok {
_spec.SetField(mediaprocesstask.FieldAttempts, field.TypeInt, value)
}
if value, ok := mptu.mutation.AddedAttempts(); ok {
_spec.AddField(mediaprocesstask.FieldAttempts, field.TypeInt, value)
}
if value, ok := mptu.mutation.Error(); ok {
_spec.SetField(mediaprocesstask.FieldError, field.TypeString, value)
}
if mptu.mutation.ErrorCleared() {
_spec.ClearField(mediaprocesstask.FieldError, field.TypeString)
}
if value, ok := mptu.mutation.ResultSize(); ok {
_spec.SetField(mediaprocesstask.FieldResultSize, field.TypeInt64, value)
}
if value, ok := mptu.mutation.AddedResultSize(); ok {
_spec.AddField(mediaprocesstask.FieldResultSize, field.TypeInt64, value)
}
if mptu.mutation.ResultSizeCleared() {
_spec.ClearField(mediaprocesstask.FieldResultSize, field.TypeInt64)
}
if value, ok := mptu.mutation.Props(); ok {
_spec.SetField(mediaprocesstask.FieldProps, field.TypeJSON, value)
}
if mptu.mutation.PropsCleared() {
_spec.ClearField(mediaprocesstask.FieldProps, field.TypeJSON)
}
if n, err = sqlgraph.UpdateNodes(ctx, mptu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{mediaprocesstask.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
mptu.mutation.done = true
return n, nil
}
// MediaProcessTaskUpdateOne is the builder for updating a single MediaProcessTask entity.
type MediaProcessTaskUpdateOne struct {
config
fields []string
hooks []Hook
mutation *MediaProcessTaskMutation
}
// SetUpdatedAt sets the "updated_at" field.
func (mptuo *MediaProcessTaskUpdateOne) SetUpdatedAt(t time.Time) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetUpdatedAt(t)
return mptuo
}
// SetDeletedAt sets the "deleted_at" field.
func (mptuo *MediaProcessTaskUpdateOne) SetDeletedAt(t time.Time) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetDeletedAt(t)
return mptuo
}
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableDeletedAt(t *time.Time) *MediaProcessTaskUpdateOne {
if t != nil {
mptuo.SetDeletedAt(*t)
}
return mptuo
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (mptuo *MediaProcessTaskUpdateOne) ClearDeletedAt() *MediaProcessTaskUpdateOne {
mptuo.mutation.ClearDeletedAt()
return mptuo
}
// SetMediaType sets the "media_type" field.
func (mptuo *MediaProcessTaskUpdateOne) SetMediaType(mt mediaprocesstask.MediaType) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetMediaType(mt)
return mptuo
}
// SetNillableMediaType sets the "media_type" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableMediaType(mt *mediaprocesstask.MediaType) *MediaProcessTaskUpdateOne {
if mt != nil {
mptuo.SetMediaType(*mt)
}
return mptuo
}
// SetStatus sets the "status" field.
func (mptuo *MediaProcessTaskUpdateOne) SetStatus(m mediaprocesstask.Status) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetStatus(m)
return mptuo
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableStatus(m *mediaprocesstask.Status) *MediaProcessTaskUpdateOne {
if m != nil {
mptuo.SetStatus(*m)
}
return mptuo
}
// SetEntityID sets the "entity_id" field.
func (mptuo *MediaProcessTaskUpdateOne) SetEntityID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.ResetEntityID()
mptuo.mutation.SetEntityID(i)
return mptuo
}
// SetNillableEntityID sets the "entity_id" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableEntityID(i *int) *MediaProcessTaskUpdateOne {
if i != nil {
mptuo.SetEntityID(*i)
}
return mptuo
}
// AddEntityID adds i to the "entity_id" field.
func (mptuo *MediaProcessTaskUpdateOne) AddEntityID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.AddEntityID(i)
return mptuo
}
// SetFileID sets the "file_id" field.
func (mptuo *MediaProcessTaskUpdateOne) SetFileID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.ResetFileID()
mptuo.mutation.SetFileID(i)
return mptuo
}
// SetNillableFileID sets the "file_id" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableFileID(i *int) *MediaProcessTaskUpdateOne {
if i != nil {
mptuo.SetFileID(*i)
}
return mptuo
}
// AddFileID adds i to the "file_id" field.
func (mptuo *MediaProcessTaskUpdateOne) AddFileID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.AddFileID(i)
return mptuo
}
// ClearFileID clears the value of the "file_id" field.
func (mptuo *MediaProcessTaskUpdateOne) ClearFileID() *MediaProcessTaskUpdateOne {
mptuo.mutation.ClearFileID()
return mptuo
}
// SetOwnerID sets the "owner_id" field.
func (mptuo *MediaProcessTaskUpdateOne) SetOwnerID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.ResetOwnerID()
mptuo.mutation.SetOwnerID(i)
return mptuo
}
// SetNillableOwnerID sets the "owner_id" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableOwnerID(i *int) *MediaProcessTaskUpdateOne {
if i != nil {
mptuo.SetOwnerID(*i)
}
return mptuo
}
// AddOwnerID adds i to the "owner_id" field.
func (mptuo *MediaProcessTaskUpdateOne) AddOwnerID(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.AddOwnerID(i)
return mptuo
}
// SetAttempts sets the "attempts" field.
func (mptuo *MediaProcessTaskUpdateOne) SetAttempts(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.ResetAttempts()
mptuo.mutation.SetAttempts(i)
return mptuo
}
// SetNillableAttempts sets the "attempts" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableAttempts(i *int) *MediaProcessTaskUpdateOne {
if i != nil {
mptuo.SetAttempts(*i)
}
return mptuo
}
// AddAttempts adds i to the "attempts" field.
func (mptuo *MediaProcessTaskUpdateOne) AddAttempts(i int) *MediaProcessTaskUpdateOne {
mptuo.mutation.AddAttempts(i)
return mptuo
}
// SetError sets the "error" field.
func (mptuo *MediaProcessTaskUpdateOne) SetError(s string) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetError(s)
return mptuo
}
// SetNillableError sets the "error" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableError(s *string) *MediaProcessTaskUpdateOne {
if s != nil {
mptuo.SetError(*s)
}
return mptuo
}
// ClearError clears the value of the "error" field.
func (mptuo *MediaProcessTaskUpdateOne) ClearError() *MediaProcessTaskUpdateOne {
mptuo.mutation.ClearError()
return mptuo
}
// SetResultSize sets the "result_size" field.
func (mptuo *MediaProcessTaskUpdateOne) SetResultSize(i int64) *MediaProcessTaskUpdateOne {
mptuo.mutation.ResetResultSize()
mptuo.mutation.SetResultSize(i)
return mptuo
}
// SetNillableResultSize sets the "result_size" field if the given value is not nil.
func (mptuo *MediaProcessTaskUpdateOne) SetNillableResultSize(i *int64) *MediaProcessTaskUpdateOne {
if i != nil {
mptuo.SetResultSize(*i)
}
return mptuo
}
// AddResultSize adds i to the "result_size" field.
func (mptuo *MediaProcessTaskUpdateOne) AddResultSize(i int64) *MediaProcessTaskUpdateOne {
mptuo.mutation.AddResultSize(i)
return mptuo
}
// ClearResultSize clears the value of the "result_size" field.
func (mptuo *MediaProcessTaskUpdateOne) ClearResultSize() *MediaProcessTaskUpdateOne {
mptuo.mutation.ClearResultSize()
return mptuo
}
// SetProps sets the "props" field.
func (mptuo *MediaProcessTaskUpdateOne) SetProps(tptp *types.MediaProcessTaskProps) *MediaProcessTaskUpdateOne {
mptuo.mutation.SetProps(tptp)
return mptuo
}
// ClearProps clears the value of the "props" field.
func (mptuo *MediaProcessTaskUpdateOne) ClearProps() *MediaProcessTaskUpdateOne {
mptuo.mutation.ClearProps()
return mptuo
}
// Mutation returns the MediaProcessTaskMutation object of the builder.
func (mptuo *MediaProcessTaskUpdateOne) Mutation() *MediaProcessTaskMutation {
return mptuo.mutation
}
// Where appends a list predicates to the MediaProcessTaskUpdate builder.
func (mptuo *MediaProcessTaskUpdateOne) Where(ps ...predicate.MediaProcessTask) *MediaProcessTaskUpdateOne {
mptuo.mutation.Where(ps...)
return mptuo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (mptuo *MediaProcessTaskUpdateOne) Select(field string, fields ...string) *MediaProcessTaskUpdateOne {
mptuo.fields = append([]string{field}, fields...)
return mptuo
}
// Save executes the query and returns the updated MediaProcessTask entity.
func (mptuo *MediaProcessTaskUpdateOne) Save(ctx context.Context) (*MediaProcessTask, error) {
if err := mptuo.defaults(); err != nil {
return nil, err
}
return withHooks(ctx, mptuo.sqlSave, mptuo.mutation, mptuo.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (mptuo *MediaProcessTaskUpdateOne) SaveX(ctx context.Context) *MediaProcessTask {
node, err := mptuo.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (mptuo *MediaProcessTaskUpdateOne) Exec(ctx context.Context) error {
_, err := mptuo.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (mptuo *MediaProcessTaskUpdateOne) ExecX(ctx context.Context) {
if err := mptuo.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (mptuo *MediaProcessTaskUpdateOne) defaults() error {
if _, ok := mptuo.mutation.UpdatedAt(); !ok {
if mediaprocesstask.UpdateDefaultUpdatedAt == nil {
return fmt.Errorf("ent: uninitialized mediaprocesstask.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
}
v := mediaprocesstask.UpdateDefaultUpdatedAt()
mptuo.mutation.SetUpdatedAt(v)
}
return nil
}
// check runs all checks and user-defined validators on the builder.
func (mptuo *MediaProcessTaskUpdateOne) check() error {
if v, ok := mptuo.mutation.MediaType(); ok {
if err := mediaprocesstask.MediaTypeValidator(v); err != nil {
return &ValidationError{Name: "media_type", err: fmt.Errorf(`ent: validator failed for field "MediaProcessTask.media_type": %w`, err)}
}
}
if v, ok := mptuo.mutation.Status(); ok {
if err := mediaprocesstask.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "MediaProcessTask.status": %w`, err)}
}
}
return nil
}
func (mptuo *MediaProcessTaskUpdateOne) sqlSave(ctx context.Context) (_node *MediaProcessTask, err error) {
if err := mptuo.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(mediaprocesstask.Table, mediaprocesstask.Columns, sqlgraph.NewFieldSpec(mediaprocesstask.FieldID, field.TypeInt))
id, ok := mptuo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MediaProcessTask.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := mptuo.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, mediaprocesstask.FieldID)
for _, f := range fields {
if !mediaprocesstask.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != mediaprocesstask.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := mptuo.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := mptuo.mutation.UpdatedAt(); ok {
_spec.SetField(mediaprocesstask.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := mptuo.mutation.DeletedAt(); ok {
_spec.SetField(mediaprocesstask.FieldDeletedAt, field.TypeTime, value)
}
if mptuo.mutation.DeletedAtCleared() {
_spec.ClearField(mediaprocesstask.FieldDeletedAt, field.TypeTime)
}
if value, ok := mptuo.mutation.MediaType(); ok {
_spec.SetField(mediaprocesstask.FieldMediaType, field.TypeEnum, value)
}
if value, ok := mptuo.mutation.Status(); ok {
_spec.SetField(mediaprocesstask.FieldStatus, field.TypeEnum, value)
}
if value, ok := mptuo.mutation.EntityID(); ok {
_spec.SetField(mediaprocesstask.FieldEntityID, field.TypeInt, value)
}
if value, ok := mptuo.mutation.AddedEntityID(); ok {
_spec.AddField(mediaprocesstask.FieldEntityID, field.TypeInt, value)
}
if value, ok := mptuo.mutation.FileID(); ok {
_spec.SetField(mediaprocesstask.FieldFileID, field.TypeInt, value)
}
if value, ok := mptuo.mutation.AddedFileID(); ok {
_spec.AddField(mediaprocesstask.FieldFileID, field.TypeInt, value)
}
if mptuo.mutation.FileIDCleared() {
_spec.ClearField(mediaprocesstask.FieldFileID, field.TypeInt)
}
if value, ok := mptuo.mutation.OwnerID(); ok {
_spec.SetField(mediaprocesstask.FieldOwnerID, field.TypeInt, value)
}
if value, ok := mptuo.mutation.AddedOwnerID(); ok {
_spec.AddField(mediaprocesstask.FieldOwnerID, field.TypeInt, value)
}
if value, ok := mptuo.mutation.Attempts(); ok {
_spec.SetField(mediaprocesstask.FieldAttempts, field.TypeInt, value)
}
if value, ok := mptuo.mutation.AddedAttempts(); ok {
_spec.AddField(mediaprocesstask.FieldAttempts, field.TypeInt, value)
}
if value, ok := mptuo.mutation.Error(); ok {
_spec.SetField(mediaprocesstask.FieldError, field.TypeString, value)
}
if mptuo.mutation.ErrorCleared() {
_spec.ClearField(mediaprocesstask.FieldError, field.TypeString)
}
if value, ok := mptuo.mutation.ResultSize(); ok {
_spec.SetField(mediaprocesstask.FieldResultSize, field.TypeInt64, value)
}
if value, ok := mptuo.mutation.AddedResultSize(); ok {
_spec.AddField(mediaprocesstask.FieldResultSize, field.TypeInt64, value)
}
if mptuo.mutation.ResultSizeCleared() {
_spec.ClearField(mediaprocesstask.FieldResultSize, field.TypeInt64)
}
if value, ok := mptuo.mutation.Props(); ok {
_spec.SetField(mediaprocesstask.FieldProps, field.TypeJSON, value)
}
if mptuo.mutation.PropsCleared() {
_spec.ClearField(mediaprocesstask.FieldProps, field.TypeJSON)
}
_node = &MediaProcessTask{config: mptuo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, mptuo.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{mediaprocesstask.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
mptuo.mutation.done = true
return _node, nil
}

@ -216,6 +216,40 @@ var (
},
},
}
// MediaProcessTasksColumns holds the columns for the "media_process_tasks" table.
MediaProcessTasksColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime"}},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime"}},
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"mysql": "datetime"}},
{Name: "media_type", Type: field.TypeEnum, Enums: []string{"image", "video"}, Default: "image"},
{Name: "status", Type: field.TypeEnum, Enums: []string{"pending", "processing", "done", "failed", "skipped"}, Default: "pending"},
{Name: "entity_id", Type: field.TypeInt},
{Name: "file_id", Type: field.TypeInt, Nullable: true},
{Name: "owner_id", Type: field.TypeInt},
{Name: "attempts", Type: field.TypeInt, Default: 0},
{Name: "error", Type: field.TypeString, Nullable: true, Size: 2147483647},
{Name: "result_size", Type: field.TypeInt64, Nullable: true},
{Name: "props", Type: field.TypeJSON, Nullable: true},
}
// MediaProcessTasksTable holds the schema information for the "media_process_tasks" table.
MediaProcessTasksTable = &schema.Table{
Name: "media_process_tasks",
Columns: MediaProcessTasksColumns,
PrimaryKey: []*schema.Column{MediaProcessTasksColumns[0]},
Indexes: []*schema.Index{
{
Name: "mediaprocesstask_status_media_type",
Unique: false,
Columns: []*schema.Column{MediaProcessTasksColumns[5], MediaProcessTasksColumns[4]},
},
{
Name: "mediaprocesstask_entity_id_status",
Unique: false,
Columns: []*schema.Column{MediaProcessTasksColumns[6], MediaProcessTasksColumns[5]},
},
},
}
// MetadataColumns holds the columns for the "metadata" table.
MetadataColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true},
@ -559,6 +593,7 @@ var (
FilesTable,
FsEventsTable,
GroupsTable,
MediaProcessTasksTable,
MetadataTable,
NodesTable,
OauthClientsTable,

File diff suppressed because it is too large Load Diff

@ -40,6 +40,12 @@ func (m *GroupMutation) SetRawID(t int) {
// SetUpdatedAt sets the "updated_at" field.
func (m *MediaProcessTaskMutation) SetRawID(t int) {
m.id = &t
}
// SetUpdatedAt sets the "updated_at" field.
func (m *MetadataMutation) SetRawID(t int) {
m.id = &t
}

@ -24,6 +24,9 @@ type FsEvent func(*sql.Selector)
// Group is the predicate function for group builders.
type Group func(*sql.Selector)
// MediaProcessTask is the predicate function for mediaprocesstask builders.
type MediaProcessTask func(*sql.Selector)
// Metadata is the predicate function for metadata builders.
type Metadata func(*sql.Selector)

@ -11,6 +11,7 @@ import (
"github.com/cloudreve/Cloudreve/v4/ent/file"
"github.com/cloudreve/Cloudreve/v4/ent/fsevent"
"github.com/cloudreve/Cloudreve/v4/ent/group"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/metadata"
"github.com/cloudreve/Cloudreve/v4/ent/node"
"github.com/cloudreve/Cloudreve/v4/ent/oauthclient"
@ -152,6 +153,29 @@ func init() {
groupDescSettings := groupFields[4].Descriptor()
// group.DefaultSettings holds the default value on creation for the settings field.
group.DefaultSettings = groupDescSettings.Default.(*types.GroupSetting)
mediaprocesstaskMixin := schema.MediaProcessTask{}.Mixin()
mediaprocesstaskMixinHooks0 := mediaprocesstaskMixin[0].Hooks()
mediaprocesstask.Hooks[0] = mediaprocesstaskMixinHooks0[0]
mediaprocesstaskMixinInters0 := mediaprocesstaskMixin[0].Interceptors()
mediaprocesstask.Interceptors[0] = mediaprocesstaskMixinInters0[0]
mediaprocesstaskMixinFields0 := mediaprocesstaskMixin[0].Fields()
_ = mediaprocesstaskMixinFields0
mediaprocesstaskFields := schema.MediaProcessTask{}.Fields()
_ = mediaprocesstaskFields
// mediaprocesstaskDescCreatedAt is the schema descriptor for created_at field.
mediaprocesstaskDescCreatedAt := mediaprocesstaskMixinFields0[0].Descriptor()
// mediaprocesstask.DefaultCreatedAt holds the default value on creation for the created_at field.
mediaprocesstask.DefaultCreatedAt = mediaprocesstaskDescCreatedAt.Default.(func() time.Time)
// mediaprocesstaskDescUpdatedAt is the schema descriptor for updated_at field.
mediaprocesstaskDescUpdatedAt := mediaprocesstaskMixinFields0[1].Descriptor()
// mediaprocesstask.DefaultUpdatedAt holds the default value on creation for the updated_at field.
mediaprocesstask.DefaultUpdatedAt = mediaprocesstaskDescUpdatedAt.Default.(func() time.Time)
// mediaprocesstask.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
mediaprocesstask.UpdateDefaultUpdatedAt = mediaprocesstaskDescUpdatedAt.UpdateDefault.(func() time.Time)
// mediaprocesstaskDescAttempts is the schema descriptor for attempts field.
mediaprocesstaskDescAttempts := mediaprocesstaskFields[5].Descriptor()
// mediaprocesstask.DefaultAttempts holds the default value on creation for the attempts field.
mediaprocesstask.DefaultAttempts = mediaprocesstaskDescAttempts.Default.(int)
metadataMixin := schema.Metadata{}.Mixin()
metadataMixinHooks0 := metadataMixin[0].Hooks()
metadata.Hooks[0] = metadataMixinHooks0[0]

@ -0,0 +1,67 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// MediaProcessTask holds the schema definition for the MediaProcessTask entity.
//
// It is a deferred media post-processing work item (APP-101): one row per blob
// pending compression. The table is shared between image and video work via the
// media_type discriminator (video is a follow-up ticket).
//
// The references to entity/file/owner are stored as plain int columns (no ent
// FK edges) on purpose: entities are hard-deleted during recycle
// (inventory/file.go), so a hard FK constraint would break that path. The task
// resolves them through the existing inventory clients and self-heals (skips)
// when the referenced blob is gone.
type MediaProcessTask struct {
ent.Schema
}
// Fields of the MediaProcessTask.
func (MediaProcessTask) Fields() []ent.Field {
return []ent.Field{
field.Enum("media_type").
Values("image", "video").
Default("image"),
field.Enum("status").
Values("pending", "processing", "done", "failed", "skipped").
Default("pending"),
// entity_id is the physical blob to process (required).
field.Int("entity_id"),
// file_id is the logical file, for navigation/write-back (optional: the
// file may be gone by processing time).
field.Int("file_id").Optional(),
// owner_id is the user that owns the blob, for quota accounting (required).
field.Int("owner_id"),
field.Int("attempts").Default(0),
field.Text("error").Optional(),
field.Int64("result_size").Optional(),
field.JSON("props", &types.MediaProcessTaskProps{}).Optional(),
}
}
// Edges of the MediaProcessTask.
func (MediaProcessTask) Edges() []ent.Edge {
return nil
}
// Indexes of the MediaProcessTask.
func (MediaProcessTask) Indexes() []ent.Index {
return []ent.Index{
// Batch pickup: ListPending filters by (status, media_type).
index.Fields("status", "media_type"),
// Idempotency guard: "is there an active row for this entity?".
index.Fields("entity_id", "status"),
}
}
func (MediaProcessTask) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

@ -26,6 +26,8 @@ type Tx struct {
FsEvent *FsEventClient
// Group is the client for interacting with the Group builders.
Group *GroupClient
// MediaProcessTask is the client for interacting with the MediaProcessTask builders.
MediaProcessTask *MediaProcessTaskClient
// Metadata is the client for interacting with the Metadata builders.
Metadata *MetadataClient
// Node is the client for interacting with the Node builders.
@ -183,6 +185,7 @@ func (tx *Tx) init() {
tx.File = NewFileClient(tx.config)
tx.FsEvent = NewFsEventClient(tx.config)
tx.Group = NewGroupClient(tx.config)
tx.MediaProcessTask = NewMediaProcessTaskClient(tx.config)
tx.Metadata = NewMetadataClient(tx.config)
tx.Node = NewNodeClient(tx.config)
tx.OAuthClient = NewOAuthClientClient(tx.config)

@ -0,0 +1,162 @@
package inventory
import (
"context"
"fmt"
"github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/conf"
)
// MediaProcessClient is the data-access layer for the media_process_task table
// (APP-101): the queue of blobs pending deferred post-processing (compression).
type MediaProcessClient interface {
TxOperator
// Enqueue registers a blob as pending for the given media type. It is
// idempotent: if an active (pending/processing) row already exists for the
// entity, it returns that row instead of creating a duplicate.
Enqueue(ctx context.Context, args *MediaProcessEnqueueArgs) (*ent.MediaProcessTask, error)
// HasActive reports whether an active (pending/processing) row exists for the
// given entity id.
HasActive(ctx context.Context, entityID int) (bool, error)
// ListPending returns up to limit pending rows of the given media type,
// oldest first.
ListPending(ctx context.Context, mediaType mediaprocesstask.MediaType, limit int) ([]*ent.MediaProcessTask, error)
// GetByID returns a row by its id.
GetByID(ctx context.Context, id int) (*ent.MediaProcessTask, error)
// SetStatus transitions a row to the given status, bumping attempts and
// persisting error/result_size/props when supplied.
SetStatus(ctx context.Context, id int, args *MediaProcessStatusArgs) (*ent.MediaProcessTask, error)
}
type (
MediaProcessEnqueueArgs struct {
EntityID int
FileID int
OwnerID int
MediaType mediaprocesstask.MediaType
Props *types.MediaProcessTaskProps
}
MediaProcessStatusArgs struct {
Status mediaprocesstask.Status
BumpAttempts bool
Error string
ResultSize int64
Props *types.MediaProcessTaskProps
}
)
func NewMediaProcessClient(client *ent.Client, dbType conf.DBType) MediaProcessClient {
return &mediaProcessClient{client: client, maxSQlParam: sqlParamLimit(dbType)}
}
type mediaProcessClient struct {
maxSQlParam int
client *ent.Client
}
func (c *mediaProcessClient) SetClient(newClient *ent.Client) TxOperator {
return &mediaProcessClient{client: newClient, maxSQlParam: c.maxSQlParam}
}
func (c *mediaProcessClient) GetClient() *ent.Client {
return c.client
}
func (c *mediaProcessClient) HasActive(ctx context.Context, entityID int) (bool, error) {
return c.client.MediaProcessTask.Query().
Where(
mediaprocesstask.EntityID(entityID),
mediaprocesstask.StatusIn(mediaprocesstask.StatusPending, mediaprocesstask.StatusProcessing),
).
Exist(ctx)
}
func (c *mediaProcessClient) Enqueue(ctx context.Context, args *MediaProcessEnqueueArgs) (*ent.MediaProcessTask, error) {
mediaType := args.MediaType
if mediaType == "" {
mediaType = mediaprocesstask.MediaTypeImage
}
// Idempotency: reuse an existing active row for this entity.
existing, err := c.client.MediaProcessTask.Query().
Where(
mediaprocesstask.EntityID(args.EntityID),
mediaprocesstask.StatusIn(mediaprocesstask.StatusPending, mediaprocesstask.StatusProcessing),
).
First(ctx)
if err == nil {
return existing, nil
}
if !ent.IsNotFound(err) {
return nil, fmt.Errorf("failed to query active media process task: %w", err)
}
stm := c.client.MediaProcessTask.Create().
SetEntityID(args.EntityID).
SetOwnerID(args.OwnerID).
SetMediaType(mediaType).
SetStatus(mediaprocesstask.StatusPending)
if args.FileID != 0 {
stm.SetFileID(args.FileID)
}
if args.Props != nil {
stm.SetProps(args.Props)
}
created, err := stm.Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed to enqueue media process task: %w", err)
}
return created, nil
}
func (c *mediaProcessClient) ListPending(ctx context.Context, mediaType mediaprocesstask.MediaType, limit int) ([]*ent.MediaProcessTask, error) {
if limit <= 0 {
limit = 50
}
if mediaType == "" {
mediaType = mediaprocesstask.MediaTypeImage
}
return c.client.MediaProcessTask.Query().
Where(
mediaprocesstask.StatusEQ(mediaprocesstask.StatusPending),
mediaprocesstask.MediaTypeEQ(mediaType),
).
Order(ent.Asc(mediaprocesstask.FieldID)).
Limit(limit).
All(ctx)
}
func (c *mediaProcessClient) GetByID(ctx context.Context, id int) (*ent.MediaProcessTask, error) {
return c.client.MediaProcessTask.Get(ctx, id)
}
func (c *mediaProcessClient) SetStatus(ctx context.Context, id int, args *MediaProcessStatusArgs) (*ent.MediaProcessTask, error) {
stm := c.client.MediaProcessTask.UpdateOneID(id).
SetStatus(args.Status)
if args.BumpAttempts {
stm.AddAttempts(1)
}
if args.Error != "" {
stm.SetError(args.Error)
}
if args.ResultSize > 0 {
stm.SetResultSize(args.ResultSize)
}
if args.Props != nil {
stm.SetProps(args.Props)
}
updated, err := stm.Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed to update media process task status: %w", err)
}
return updated, nil
}

@ -0,0 +1,73 @@
package inventory
import (
"context"
"testing"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestMediaProcessEnqueueIdempotent covers APP-101: Enqueue is idempotent per
// active entity, ListPending filters by status+media_type, and SetStatus(done)
// clears the active guard so a fresh Enqueue is allowed again.
func TestMediaProcessEnqueueIdempotent(t *testing.T) {
ctx := context.Background()
client := newTestClient(t)
c := NewMediaProcessClient(client, "sqlite")
// First enqueue creates a pending row.
row1, err := c.Enqueue(ctx, &MediaProcessEnqueueArgs{
EntityID: 101, FileID: 11, OwnerID: 1, MediaType: mediaprocesstask.MediaTypeImage,
})
require.NoError(t, err)
assert.Equal(t, mediaprocesstask.StatusPending, row1.Status)
assert.Equal(t, 101, row1.EntityID)
// Second enqueue for the same entity returns the same row (no duplicate).
row2, err := c.Enqueue(ctx, &MediaProcessEnqueueArgs{
EntityID: 101, FileID: 11, OwnerID: 1, MediaType: mediaprocesstask.MediaTypeImage,
})
require.NoError(t, err)
assert.Equal(t, row1.ID, row2.ID, "duplicate enqueue must reuse the active row")
active, err := c.HasActive(ctx, 101)
require.NoError(t, err)
assert.True(t, active)
// A different entity is a separate row.
_, err = c.Enqueue(ctx, &MediaProcessEnqueueArgs{
EntityID: 202, FileID: 22, OwnerID: 1, MediaType: mediaprocesstask.MediaTypeImage,
})
require.NoError(t, err)
// ListPending returns both pending image rows.
pending, err := c.ListPending(ctx, mediaprocesstask.MediaTypeImage, 50)
require.NoError(t, err)
assert.Len(t, pending, 2)
// Video pending is empty (discriminator filter).
vids, err := c.ListPending(ctx, mediaprocesstask.MediaTypeVideo, 50)
require.NoError(t, err)
assert.Len(t, vids, 0)
// Marking entity 101 done clears the active guard and drops it from pending.
_, err = c.SetStatus(ctx, row1.ID, &MediaProcessStatusArgs{Status: mediaprocesstask.StatusDone, ResultSize: 1234})
require.NoError(t, err)
active, err = c.HasActive(ctx, 101)
require.NoError(t, err)
assert.False(t, active, "done row must not count as active")
pending, err = c.ListPending(ctx, mediaprocesstask.MediaTypeImage, 50)
require.NoError(t, err)
assert.Len(t, pending, 1, "only the still-pending entity remains")
// After done, a new enqueue for the same entity is allowed (re-upload case).
row3, err := c.Enqueue(ctx, &MediaProcessEnqueueArgs{
EntityID: 101, FileID: 11, OwnerID: 1, MediaType: mediaprocesstask.MediaTypeImage,
})
require.NoError(t, err)
assert.NotEqual(t, row1.ID, row3.ID, "a new pending row is created after the previous one is done")
}

@ -625,6 +625,26 @@ var DefaultSettings = map[string]string{
"queue_remote_download_backoff_max_duration": "600",
"queue_remote_download_max_retry": "5",
"queue_remote_download_retry_delay": "0",
// APP-101 — media post-processing (image compression) queue tuning + cron.
"queue_media_process_max_execution": "3600",
"queue_media_process_backoff_factor": "2",
"queue_media_process_backoff_max_duration": "60",
"queue_media_process_max_retry": "3",
"queue_media_process_retry_delay": "5",
"cron_media_process": "@every 1h",
// Master switch + compression parameters. Engine defaults to vips (better for
// stills; present in the deploy image alongside ffmpeg). Worker count uses a
// dedicated key (the generic queue worker_num getter has an upstream key bug),
// default 1 to keep CPU usage bounded.
"media_compress_image_enabled": "0",
"media_compress_engine": "vips",
"media_compress_worker_num": "1",
"media_compress_batch_size": "50",
"media_compress_image_quality": "80",
"media_compress_image_format": "keep",
"media_compress_image_args": "",
"media_compress_result_mode": "version",
"media_compress_min_size": "204800", // 200 KB
"entity_url_default_ttl": "3600",
"entity_url_cache_margin": "600",
"media_meta": "1",

@ -17,6 +17,20 @@ type (
DisableViewSync bool `json:"disable_view_sync,omitempty"`
FsViewMap map[string]ExplorerView `json:"fs_view_map,omitempty"`
ShareLinksInProfile ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"`
// AutoCompressImages opts this user's uploaded images into the deferred
// media post-processing (compression) pipeline (APP-101). Default off.
AutoCompressImages bool `json:"auto_compress_images,omitempty"`
}
// MediaProcessTaskProps is the JSON payload of a media_process_task row
// (APP-101). Signature is the idempotency fingerprint (engine+quality+format
// +original size) so a blob is not recompressed with the same parameters.
MediaProcessTaskProps struct {
Engine string `json:"engine,omitempty"`
Quality int `json:"quality,omitempty"`
Format string `json:"format,omitempty"`
OriginalSize int64 `json:"original_size,omitempty"`
Signature string `json:"signature,omitempty"`
}
ShareLinksInProfileLevel string

@ -0,0 +1,506 @@
package manager
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/cloudreve/Cloudreve/v4/application/dependency"
"github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/ent/mediaprocesstask"
"github.com/cloudreve/Cloudreve/v4/ent/task"
"github.com/cloudreve/Cloudreve/v4/inventory"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/crontab"
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/dbfs"
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
"github.com/cloudreve/Cloudreve/v4/pkg/queue"
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
"github.com/cloudreve/Cloudreve/v4/pkg/util"
"github.com/gofrs/uuid"
)
// skipMediaProcessEnqueueKey marks a context as originating from the media
// compression write-back, so CompleteUpload does not re-enqueue a compression
// task for the freshly written (already compressed) version — which would loop.
type skipMediaProcessEnqueueKey struct{}
func withSkipMediaProcessEnqueue(ctx context.Context) context.Context {
return context.WithValue(ctx, skipMediaProcessEnqueueKey{}, true)
}
func shouldSkipMediaProcessEnqueue(ctx context.Context) bool {
v, _ := ctx.Value(skipMediaProcessEnqueueKey{}).(bool)
return v
}
const mediaCompressTempFolder = "media_compress"
type (
// MediaCompressTask is the queue task that compresses a single blob recorded
// in the media_process_task table (APP-101). One task == one image.
MediaCompressTask struct {
*queue.DBTask
l logging.Logger
state *MediaCompressTaskState
}
MediaCompressTaskState struct {
// RowID is the media_process_task row id this task processes.
RowID int `json:"row_id"`
}
)
func init() {
queue.RegisterResumableTaskFactory(queue.MediaCompressTaskType, NewMediaCompressTaskFromModel)
// Cron: enqueue a bounded batch of pending images onto the dedicated
// MediaProcessQueue. Mirrors pkg/filemanager/manager/recycle.go.
crontab.Register(setting.CronTypeMediaProcess, func(ctx context.Context) {
dep := dependency.FromContext(ctx)
l := dep.Logger()
mp := dep.SettingProvider().MediaProcess(ctx)
if !mp.ImageEnabled {
return
}
rows, err := dep.MediaProcessClient().ListPending(ctx, mediaprocesstask.MediaTypeImage, mp.BatchSize)
if err != nil {
l.Error("Failed to list pending media process tasks: %s", err)
return
}
if len(rows) == 0 {
return
}
q := dep.MediaProcessQueue(ctx)
uc := dep.UserClient()
enqueued := 0
for _, row := range rows {
// The task must carry an owner (DBTask.Owner() is dereferenced when the
// queue persists the task); attribute it to the blob's owner.
owner, err := uc.GetByID(ctx, row.OwnerID)
if err != nil {
l.Error("Failed to load owner %d for media compress row %d: %s", row.OwnerID, row.ID, err)
continue
}
t, err := NewMediaCompressTask(ctx, row.ID, owner)
if err != nil {
l.Error("Failed to create media compress task for row %d: %s", row.ID, err)
continue
}
if err := q.QueueTask(ctx, t); err != nil {
l.Error("Failed to queue media compress task for row %d: %s", row.ID, err)
continue
}
enqueued++
}
l.Info("Enqueued %d media compress task(s) from cron.", enqueued)
})
}
func NewMediaCompressTask(ctx context.Context, rowID int, owner *ent.User) (queue.Task, error) {
state := &MediaCompressTaskState{RowID: rowID}
stateBytes, err := json.Marshal(state)
if err != nil {
return nil, fmt.Errorf("failed to marshal state: %w", err)
}
return &MediaCompressTask{
DBTask: &queue.DBTask{
// DirectOwner must be non-nil: the queue dereferences Owner().ID when
// persisting the task (pkg/queue/task.go).
DirectOwner: owner,
Task: &ent.Task{
Type: queue.MediaCompressTaskType,
CorrelationID: logging.CorrelationID(ctx),
PrivateState: string(stateBytes),
PublicState: &types.TaskPublicState{},
},
},
}, nil
}
func NewMediaCompressTaskFromModel(t *ent.Task) queue.Task {
return &MediaCompressTask{
DBTask: &queue.DBTask{
Task: t,
},
}
}
func (m *MediaCompressTask) Do(ctx context.Context) (task.Status, error) {
dep := dependency.FromContext(ctx)
m.l = dep.Logger()
state := &MediaCompressTaskState{}
if err := json.Unmarshal([]byte(m.State()), state); err != nil {
return task.StatusError, fmt.Errorf("failed to unmarshal state: %w", err)
}
m.state = state
mpClient := dep.MediaProcessClient()
row, err := mpClient.GetByID(ctx, state.RowID)
if err != nil {
// Row gone (e.g. cleaned up): nothing to do.
m.l.Warning("Media process task row %d not found, skipping: %s", state.RowID, err)
return task.StatusCompleted, nil
}
// Idempotency: only act on a pending row.
if row.Status != mediaprocesstask.StatusPending {
return task.StatusCompleted, nil
}
settings := dep.SettingProvider().MediaProcess(ctx)
if !settings.ImageEnabled {
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped})
return task.StatusCompleted, nil
}
// Only the write-back "version" mode is implemented in the MVP; replace/auto
// are follow-ups (see ticket §7.1). Any other value falls back to version.
if _, err := mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{
Status: mediaprocesstask.StatusProcessing,
BumpAttempts: true,
}); err != nil {
return task.StatusError, fmt.Errorf("failed to mark processing: %w", err)
}
if err := m.compress(ctx, dep, settings, row); err != nil {
// Persist the error; the queue backoff will retry up to MaxRetry, after
// which the row stays "processing" — a follow-up sweep can requeue it.
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{
Status: mediaprocesstask.StatusFailed,
Error: err.Error(),
})
return task.StatusError, err
}
return task.StatusCompleted, nil
}
// compress runs the full pipeline for one row: read → compress → write-back as a
// new version → mark done. Returns nil on success or a documented skip.
func (m *MediaCompressTask) compress(ctx context.Context, dep dependency.Dep, settings *setting.MediaProcessSetting, row *ent.MediaProcessTask) error {
mpClient := dep.MediaProcessClient()
owner, err := dep.UserClient().GetByID(ctx, row.OwnerID)
if err != nil {
return fmt.Errorf("failed to load owner %d: %w", row.OwnerID, err)
}
fm := NewFileManager(dep, owner)
defer fm.Recycle()
es, err := fm.GetEntitySource(ctx, row.EntityID)
if err != nil {
// Blob gone (recycled/deleted): self-heal by skipping.
m.l.Warning("Entity %d unavailable, skipping media compress: %s", row.EntityID, err)
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped, Error: err.Error()})
return nil
}
defer es.Close()
originalSize := es.Entity().Size()
if originalSize < settings.MinSize {
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped})
return nil
}
// Resolve the logical file for the write-back URI + name/ext.
file, err := fm.TraverseFile(ctx, row.FileID)
if err != nil {
m.l.Warning("File %d unavailable, skipping media compress: %s", row.FileID, err)
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped, Error: err.Error()})
return nil
}
sourceExt := strings.ToLower(strings.TrimPrefix(filepath.Ext(file.Name()), "."))
// Idempotency signature: engine + quality + format + original size.
signature := fmt.Sprintf("%s|q%d|%s|%d", settings.Engine, settings.Quality, settings.Format, originalSize)
if row.Props != nil && row.Props.Signature == signature {
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped})
return nil
}
// Materialize a local input path (download if the blob is remote/encrypted).
tempDir := filepath.Join(util.DataPath(dep.SettingProvider().TempPath(ctx)), mediaCompressTempFolder)
if err := util.CreatNestedFolder(tempDir); err != nil {
return fmt.Errorf("failed to create temp folder: %w", err)
}
inputPath, cleanupInput, err := materializeLocalInput(ctx, es, tempDir, sourceExt)
if err != nil {
return fmt.Errorf("failed to materialize input: %w", err)
}
defer cleanupInput()
// Compress.
outputPath, targetExt, err := compressImage(ctx, settings, dep.SettingProvider(), inputPath, sourceExt, tempDir)
if err != nil {
if err == errUnsupportedFormat {
m.l.Info("Unsupported image format %q for row %d, skipping.", sourceExt, row.ID)
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{Status: mediaprocesstask.StatusSkipped})
return nil
}
return err
}
defer os.Remove(outputPath)
outInfo, err := os.Stat(outputPath)
if err != nil {
return fmt.Errorf("failed to stat compressed output: %w", err)
}
resultSize := outInfo.Size()
// No gain (or larger): keep the original, mark done with the signature so it
// is not retried with the same parameters.
if resultSize == 0 || resultSize >= originalSize {
m.l.Info("Compression yielded no gain for row %d (%d -> %d), keeping original.", row.ID, originalSize, resultSize)
_, _ = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{
Status: mediaprocesstask.StatusDone,
ResultSize: originalSize,
Props: &types.MediaProcessTaskProps{
Engine: settings.Engine, Quality: settings.Quality, Format: settings.Format,
OriginalSize: originalSize, Signature: signature,
},
})
return nil
}
// Write-back as a new version via manager.Update. This reuses the whole upload
// path (any driver, incl. remote) and applies the quota StorageDiff
// automatically. The context guard prevents CompleteUpload from re-enqueuing.
outFile, err := os.Open(outputPath)
if err != nil {
return fmt.Errorf("failed to open compressed output: %w", err)
}
defer outFile.Close()
req := &fs.UploadRequest{
Props: &fs.UploadProps{
Uri: file.Uri(false),
Size: resultSize,
},
File: outFile,
Seeker: outFile,
Mode: fs.ModeOverwrite,
}
writeCtx := withSkipMediaProcessEnqueue(dbfs.WithBypassOwnerCheck(ctx))
if _, err := fm.Update(writeCtx, req, fs.WithEntityType(types.EntityTypeVersion)); err != nil {
return fmt.Errorf("failed to write back compressed version: %w", err)
}
_, err = mpClient.SetStatus(ctx, row.ID, &inventory.MediaProcessStatusArgs{
Status: mediaprocesstask.StatusDone,
ResultSize: resultSize,
Props: &types.MediaProcessTaskProps{
Engine: settings.Engine, Quality: settings.Quality, Format: settings.Format,
OriginalSize: originalSize, Signature: signature,
},
})
if err != nil {
return fmt.Errorf("failed to mark done: %w", err)
}
m.l.Info("Compressed row %d: %s %d -> %d bytes (target .%s).", row.ID, sourceExt, originalSize, resultSize, targetExt)
return nil
}
// materializeLocalInput returns a local filesystem path for the entity bytes. If
// the source is a local, unencrypted blob it uses its path directly (no cleanup);
// otherwise it streams the source into a temp file (cleaned up by the returned fn).
func materializeLocalInput(ctx context.Context, es entitySourceReader, tempDir, ext string) (string, func(), error) {
if es.IsLocal() && !es.Entity().Encrypted() {
return es.LocalPath(ctx), func() {}, nil
}
name := fmt.Sprintf("in_%s.%s", uuid.Must(uuid.NewV4()).String(), ext)
tempInput := filepath.Join(tempDir, name)
f, err := util.CreatNestedFile(tempInput)
if err != nil {
return "", func() {}, fmt.Errorf("failed to create temp input: %w", err)
}
if _, err := io.Copy(f, es); err != nil {
f.Close()
os.Remove(tempInput)
return "", func() {}, fmt.Errorf("failed to download entity: %w", err)
}
f.Close()
return tempInput, func() { os.Remove(tempInput) }, nil
}
var errUnsupportedFormat = fmt.Errorf("unsupported image format")
// compressImage runs the configured engine on a local input file and returns the
// compressed output path + its extension. Engine default is vips (better for
// stills); ffmpeg is the alternative.
func compressImage(ctx context.Context, mp *setting.MediaProcessSetting, settings setting.Provider, inputPath, sourceExt, tempDir string) (string, string, error) {
targetExt := normalizeFormat(mp.Format, sourceExt)
if targetExt == "" {
return "", "", errUnsupportedFormat
}
outputPath := filepath.Join(tempDir, fmt.Sprintf("out_%s.%s", uuid.Must(uuid.NewV4()).String(), targetExt))
var (
bin string
args []string
)
switch strings.ToLower(mp.Engine) {
case "ffmpeg":
bin = settings.FFMpegPath(ctx)
a, err := ffmpegCompressArgs(inputPath, outputPath, targetExt, mp.Quality)
if err != nil {
return "", "", err
}
args = a
default: // vips
bin = settings.VipsPath(ctx)
args = []string{"copy", inputPath, vipsOutputSpec(outputPath, targetExt, mp.Quality)}
}
if extra := strings.TrimSpace(mp.ExtraArgs); extra != "" {
args = append(args, strings.Fields(extra)...)
}
cmd := exec.CommandContext(ctx, bin, args...)
var stdErr bytes.Buffer
cmd.Stderr = &stdErr
if err := cmd.Run(); err != nil {
os.Remove(outputPath)
return "", "", fmt.Errorf("failed to invoke %s: %w, output: %s", bin, err, stdErr.String())
}
return outputPath, targetExt, nil
}
// normalizeFormat resolves the target extension from the format setting. "keep"
// preserves the source extension (only for compressible raster types). An empty
// return means the source is not compressible with this configuration.
func normalizeFormat(format, sourceExt string) string {
f := strings.ToLower(strings.TrimSpace(format))
if f == "" || f == "keep" {
f = sourceExt
}
switch f {
case "jpg", "jpeg":
return "jpg"
case "webp":
return "webp"
case "png":
return "png"
default:
return ""
}
}
// vipsOutputSpec builds the vips output filename with save options that trigger
// re-encoding at the requested quality.
func vipsOutputSpec(outputPath, targetExt string, quality int) string {
switch targetExt {
case "jpg", "webp":
return fmt.Sprintf("%s[Q=%d]", outputPath, quality)
case "png":
return fmt.Sprintf("%s[compression=9]", outputPath)
default:
return outputPath
}
}
// ffmpegCompressArgs maps the quality (1..100, higher = better) onto ffmpeg's
// per-codec quality flag.
func ffmpegCompressArgs(inputPath, outputPath, targetExt string, quality int) ([]string, error) {
base := []string{"-y", "-i", inputPath}
switch targetExt {
case "jpg":
// mjpeg qscale: 2 (best) .. 31 (worst).
q := 31 - int(float64(quality)/100.0*29.0)
if q < 2 {
q = 2
}
if q > 31 {
q = 31
}
return append(base, "-q:v", strconv.Itoa(q), outputPath), nil
case "webp":
return append(base, "-quality", strconv.Itoa(quality), outputPath), nil
case "png":
return append(base, "-compression_level", "100", outputPath), nil
default:
return nil, errUnsupportedFormat
}
}
// entitySourceReader is the subset of entitysource.EntitySource the compression
// pipeline consumes (kept local to avoid widening the import surface).
type entitySourceReader interface {
io.Reader
IsLocal() bool
LocalPath(ctx context.Context) string
Entity() fs.Entity
}
// enqueueMediaProcessIfEligible registers the just-uploaded primary entity as a
// pending image-compression row, when the global switch + the owner's opt-in +
// mime/size gates all pass (APP-101). Called from CompleteUpload on the master.
// It is a no-op on the compression write-back path (context guard) so it never
// loops on its own output.
func (m *manager) enqueueMediaProcessIfEligible(ctx context.Context, session *fs.UploadSession, file fs.File) {
if m.stateless || file == nil || m.user == nil || m.user.Settings == nil {
return
}
if shouldSkipMediaProcessEnqueue(ctx) {
return
}
mp := m.settings.MediaProcess(ctx)
if !mp.ImageEnabled || !m.user.Settings.AutoCompressImages {
return
}
mimeType := ""
if session != nil && session.Props != nil {
mimeType = session.Props.MimeType
}
if mimeType == "" {
mimeType = m.dep.MimeDetector(ctx).TypeByName(file.Name())
}
if !strings.HasPrefix(strings.ToLower(mimeType), "image/") {
return
}
entity := file.PrimaryEntity()
if entity == nil || entity.Size() < mp.MinSize {
return
}
mpClient := m.dep.MediaProcessClient()
if active, err := mpClient.HasActive(ctx, entity.ID()); err != nil {
m.l.Warning("media process: active-row check failed for entity %d: %s", entity.ID(), err)
return
} else if active {
return
}
if _, err := mpClient.Enqueue(ctx, &inventory.MediaProcessEnqueueArgs{
EntityID: entity.ID(),
FileID: file.ID(),
OwnerID: m.user.ID,
MediaType: mediaprocesstask.MediaTypeImage,
}); err != nil {
m.l.Warning("media process: failed to enqueue pending row for entity %d: %s", entity.ID(), err)
}
}

@ -318,6 +318,8 @@ func (m *manager) CompleteUpload(ctx context.Context, session *fs.UploadSession)
}
m.onNewEntityUploaded(ctx, session, d, ownerId)
// APP-101: register the new image for deferred compression when eligible.
m.enqueueMediaProcessIfEligible(ctx, session, file)
// Remove upload session
_ = m.kv.Delete(UploadSessionCachePrefix, session.Props.UploadSessionID)
return file, nil

@ -104,6 +104,7 @@ const (
RelocateTaskType = "relocate"
RemoteDownloadTaskType = "remote_download"
ImportTaskType = "import"
MediaCompressTaskType = "media_compress"
FullTextIndexTaskType = "full_text_index"
FullTextCopyTaskType = "full_text_copy"

@ -156,6 +156,8 @@ type (
MusicCoverThumbExts(ctx context.Context) []string
// Cron returns the crontab settings.
Cron(ctx context.Context, t CronType) string
// MediaProcess returns the media post-processing (image compression) settings.
MediaProcess(ctx context.Context) *MediaProcessSetting
// Theme returns the theme settings.
Theme(ctx context.Context) *Theme
// Logo returns the logo settings.
@ -439,6 +441,24 @@ func (s *settingProvider) Cron(ctx context.Context, t CronType) string {
return s.getString(ctx, "cron_"+string(t), "@hourly")
}
func (s *settingProvider) MediaProcess(ctx context.Context) *MediaProcessSetting {
workerNum := s.getInt(ctx, "media_compress_worker_num", 1)
if workerNum <= 0 {
workerNum = 1
}
return &MediaProcessSetting{
ImageEnabled: s.getBoolean(ctx, "media_compress_image_enabled", false),
Engine: s.getString(ctx, "media_compress_engine", "vips"),
WorkerNum: workerNum,
BatchSize: s.getInt(ctx, "media_compress_batch_size", 50),
Quality: s.getInt(ctx, "media_compress_image_quality", 80),
Format: s.getString(ctx, "media_compress_image_format", "keep"),
ExtraArgs: s.getString(ctx, "media_compress_image_args", ""),
ResultMode: s.getString(ctx, "media_compress_result_mode", "version"),
MinSize: s.getInt64(ctx, "media_compress_min_size", 204800),
}
}
func (s *settingProvider) BuiltinThumbGeneratorEnabled(ctx context.Context) bool {
return s.getBoolean(ctx, "thumb_builtin_enabled", true)
}

@ -89,6 +89,20 @@ type (
MaxRetry int
RetryDelay time.Duration
}
// MediaProcessSetting carries the media post-processing (image compression)
// parameters (APP-101), all editable from the admin panel.
MediaProcessSetting struct {
ImageEnabled bool // master switch for image compression
Engine string // "vips" | "ffmpeg"
WorkerNum int // compression concurrency (dedicated key; default 1)
BatchSize int // max rows the cron enqueues per run
Quality int // 1..100
Format string // "keep" | "webp" | "jpeg" | "png"
ExtraArgs string // extra engine flags
ResultMode string // "version" | "replace" | "auto"
MinSize int64 // skip blobs smaller than this (bytes)
}
)
type ThumbEncode struct {
@ -103,6 +117,7 @@ var (
QueueTypeEntityRecycle = QueueType("recycle")
QueueTypeSlave = QueueType("slave")
QueueTypeRemoteDownload = QueueType("remote_download")
QueueTypeMediaProcess = QueueType("media_process")
)
type CronType string
@ -111,6 +126,7 @@ var (
CronTypeEntityCollect = CronType("entity_collect")
CronTypeTrashBinCollect = CronType("trash_bin_collect")
CronTypeOauthCredRefresh = CronType("oauth_cred_refresh")
CronTypeMediaProcess = CronType("media_process")
)
type Theme struct {

@ -262,6 +262,12 @@ var (
"queue_remote_download_backoff_max_duration": remoteDownloadQueuePostProcessor,
"queue_remote_download_max_retry": remoteDownloadQueuePostProcessor,
"queue_remote_download_retry_delay": remoteDownloadQueuePostProcessor,
"media_compress_worker_num": mediaProcessQueuePostProcessor,
"queue_media_process_max_execution": mediaProcessQueuePostProcessor,
"queue_media_process_backoff_factor": mediaProcessQueuePostProcessor,
"queue_media_process_backoff_max_duration": mediaProcessQueuePostProcessor,
"queue_media_process_max_retry": mediaProcessQueuePostProcessor,
"queue_media_process_retry_delay": mediaProcessQueuePostProcessor,
"secret_key": secretKeyPostProcessor,
"fts_meilisearch_embed_config": meilisearchPostProcessor,
"fts_meilisearch_endpoint": meilisearchPostProcessor,
@ -413,6 +419,12 @@ func thumbQueuePostProcessor(ctx context.Context, settings map[string]string) er
return nil
}
func mediaProcessQueuePostProcessor(ctx context.Context, settings map[string]string) error {
dep := dependency.FromContext(ctx)
dep.MediaProcessQueue(context.WithValue(ctx, dependency.ReloadCtx{}, true)).Start()
return nil
}
func secretKeyPostProcessor(ctx context.Context, settings map[string]string) error {
dep := dependency.FromContext(ctx)
dep.KV().Delete(manager.EntityUrlCacheKeyPrefix)

@ -27,6 +27,7 @@ func GetQueueMetrics(c *gin.Context) ([]QueueMetric, error) {
ioIntense := dep.IoIntenseQueue(c)
remoteDownload := dep.RemoteDownloadQueue(c)
thumb := dep.ThumbQueue(c)
mediaProcess := dep.MediaProcessQueue(c)
res = append(res, QueueMetric{
Name: setting.QueueTypeMediaMeta,
@ -68,6 +69,14 @@ func GetQueueMetrics(c *gin.Context) ([]QueueMetric, error) {
SubmittedTasks: thumb.SubmittedTasks(),
SuspendingTasks: thumb.SuspendingTasks(),
})
res = append(res, QueueMetric{
Name: setting.QueueTypeMediaProcess,
BusyWorkers: mediaProcess.BusyWorkers(),
SuccessTasks: mediaProcess.SuccessTasks(),
FailureTasks: mediaProcess.FailureTasks(),
SubmittedTasks: mediaProcess.SubmittedTasks(),
SuspendingTasks: mediaProcess.SuspendingTasks(),
})
return res, nil
}

@ -32,6 +32,7 @@ type UserSettings struct {
Passkeys []Passkey `json:"passkeys,omitempty"`
DisableViewSync bool `json:"disable_view_sync"`
ShareLinksInProfile string `json:"share_links_in_profile"`
AutoCompressImages bool `json:"auto_compress_images"`
OAuthGrants []OauthGrant `json:"oauth_grants,omitempty"`
}
@ -47,6 +48,7 @@ func BuildUserSettings(u *ent.User, passkeys []*ent.Passkey, parser *uaparser.Pa
}),
DisableViewSync: u.Settings.DisableViewSync,
ShareLinksInProfile: string(u.Settings.ShareLinksInProfile),
AutoCompressImages: u.Settings.AutoCompressImages,
OAuthGrants: lo.Map(grants, func(item *ent.OAuthGrant, index int) OauthGrant {
return BuildOauthGrant(item)
}),

@ -230,6 +230,7 @@ type (
TwoFACode *string `json:"two_fa_code" binding:"omitempty"`
DisableViewSync *bool `json:"disable_view_sync" binding:"omitempty"`
ShareLinksInProfile *string `json:"share_links_in_profile" binding:"omitempty"`
AutoCompressImages *bool `json:"auto_compress_images" binding:"omitempty"`
}
PatchUserSettingParamsCtx struct{}
)
@ -276,6 +277,11 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error {
saveSetting = true
}
if s.AutoCompressImages != nil {
u.Settings.AutoCompressImages = *s.AutoCompressImages
saveSetting = true
}
if s.ShareLinksInProfile != nil {
u.Settings.ShareLinksInProfile = types.ShareLinksInProfileLevel(*s.ShareLinksInProfile)
saveSetting = true

Loading…
Cancel
Save