diff --git a/pkg/filemanager/driver/handler.go b/pkg/filemanager/driver/handler.go index e6bbddc..8bfc6c6 100644 --- a/pkg/filemanager/driver/handler.go +++ b/pkg/filemanager/driver/handler.go @@ -105,6 +105,8 @@ type ( ThumbMaxSize int64 // ThumbProxy indicates whether to generate thumbnails using local generators. ThumbProxy bool + // BrowserRelayedDownload indicates whether to relay download via stream-saver. + BrowserRelayedDownload bool } ) diff --git a/pkg/filemanager/driver/onedrive/onedrive.go b/pkg/filemanager/driver/onedrive/onedrive.go index 5ac5ecf..3417365 100644 --- a/pkg/filemanager/driver/onedrive/onedrive.go +++ b/pkg/filemanager/driver/onedrive/onedrive.go @@ -4,6 +4,10 @@ import ( "context" "errors" "fmt" + "os" + "strings" + "time" + "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" @@ -16,10 +20,6 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/request" "github.com/cloudreve/Cloudreve/v4/pkg/serializer" "github.com/cloudreve/Cloudreve/v4/pkg/setting" - "net/url" - "os" - "strings" - "time" ) // Driver OneDrive 适配器 @@ -150,11 +150,6 @@ func (handler *Driver) Source(ctx context.Context, e fs.Entity, args *driver.Get return "", err } - if args.IsDownload && handler.policy.Settings.StreamSaver { - downloadUrl := res.DownloadURL + "&" + streamSaverParam + "=" + url.QueryEscape(args.DisplayName) - return downloadUrl, nil - } - return res.DownloadURL, nil } @@ -229,12 +224,13 @@ func (handler *Driver) CompleteUpload(ctx context.Context, session *fs.UploadSes func (handler *Driver) Capabilities() *driver.Capabilities { return &driver.Capabilities{ - StaticFeatures: features, - ThumbSupportedExts: handler.policy.Settings.ThumbExts, - ThumbSupportAllExts: handler.policy.Settings.ThumbSupportAllExts, - ThumbMaxSize: handler.policy.Settings.ThumbMaxSize, - ThumbProxy: handler.policy.Settings.ThumbGeneratorProxy, - MediaMetaProxy: handler.policy.Settings.MediaMetaGeneratorProxy, + StaticFeatures: features, + ThumbSupportedExts: handler.policy.Settings.ThumbExts, + ThumbSupportAllExts: handler.policy.Settings.ThumbSupportAllExts, + ThumbMaxSize: handler.policy.Settings.ThumbMaxSize, + ThumbProxy: handler.policy.Settings.ThumbGeneratorProxy, + MediaMetaProxy: handler.policy.Settings.MediaMetaGeneratorProxy, + BrowserRelayedDownload: handler.policy.Settings.StreamSaver, } } diff --git a/pkg/filemanager/fs/uri.go b/pkg/filemanager/fs/uri.go index 866a614..584b0c5 100644 --- a/pkg/filemanager/fs/uri.go +++ b/pkg/filemanager/fs/uri.go @@ -22,7 +22,7 @@ const ( const ( QuerySearchName = "name" - QuerySearchNameOpOr = "use_or" + QuerySearchNameOpOr = "name_op_or" QuerySearchMetadataPrefix = "meta_" QuerySearchCaseFolding = "case_folding" QuerySearchType = "type" diff --git a/pkg/filemanager/manager/entity.go b/pkg/filemanager/manager/entity.go index dfdf37e..6ffe058 100644 --- a/pkg/filemanager/manager/entity.go +++ b/pkg/filemanager/manager/entity.go @@ -19,33 +19,39 @@ import ( "github.com/samber/lo" ) -type EntityManagement interface { - // GetEntityUrls gets download urls of given entities, return URLs and the earliest expiry time - GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, opts ...fs.Option) ([]string, *time.Time, error) - // GetUrlForRedirectedDirectLink gets redirected direct download link of given direct link - GetUrlForRedirectedDirectLink(ctx context.Context, dl *ent.DirectLink, opts ...fs.Option) (string, *time.Time, error) - // GetDirectLink gets permanent direct download link of given files - GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectLink, error) - // GetEntitySource gets source of given entity - GetEntitySource(ctx context.Context, entityID int, opts ...fs.Option) (entitysource.EntitySource, error) - // Thumbnail gets thumbnail entity of given file - Thumbnail(ctx context.Context, uri *fs.URI) (entitysource.EntitySource, error) - // SubmitAndAwaitThumbnailTask submits a thumbnail task and waits for result - SubmitAndAwaitThumbnailTask(ctx context.Context, uri *fs.URI, ext string, entity fs.Entity) (fs.Entity, error) - // SetCurrentVersion sets current version of given file - SetCurrentVersion(ctx context.Context, path *fs.URI, version int) error - // DeleteVersion deletes a version of given file - DeleteVersion(ctx context.Context, path *fs.URI, version int) error - // ExtractAndSaveMediaMeta extracts and saves media meta into file metadata of given file. - ExtractAndSaveMediaMeta(ctx context.Context, uri *fs.URI, entityID int) error - // RecycleEntities recycles a group of entities - RecycleEntities(ctx context.Context, force bool, entityIDs ...int) error -} +type ( + EntityManagement interface { + // GetEntityUrls gets download urls of given entities, return URLs and the earliest expiry time + GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, opts ...fs.Option) ([]EntityUrl, *time.Time, error) + // GetUrlForRedirectedDirectLink gets redirected direct download link of given direct link + GetUrlForRedirectedDirectLink(ctx context.Context, dl *ent.DirectLink, opts ...fs.Option) (string, *time.Time, error) + // GetDirectLink gets permanent direct download link of given files + GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectLink, error) + // GetEntitySource gets source of given entity + GetEntitySource(ctx context.Context, entityID int, opts ...fs.Option) (entitysource.EntitySource, error) + // Thumbnail gets thumbnail entity of given file + Thumbnail(ctx context.Context, uri *fs.URI) (entitysource.EntitySource, error) + // SubmitAndAwaitThumbnailTask submits a thumbnail task and waits for result + SubmitAndAwaitThumbnailTask(ctx context.Context, uri *fs.URI, ext string, entity fs.Entity) (fs.Entity, error) + // SetCurrentVersion sets current version of given file + SetCurrentVersion(ctx context.Context, path *fs.URI, version int) error + // DeleteVersion deletes a version of given file + DeleteVersion(ctx context.Context, path *fs.URI, version int) error + // ExtractAndSaveMediaMeta extracts and saves media meta into file metadata of given file. + ExtractAndSaveMediaMeta(ctx context.Context, uri *fs.URI, entityID int) error + // RecycleEntities recycles a group of entities + RecycleEntities(ctx context.Context, force bool, entityIDs ...int) error + } + DirectLink struct { + File fs.File + Url string + } -type DirectLink struct { - File fs.File - Url string -} + EntityUrl struct { + Url string `json:"url"` + BrowserDownloadDisplayName string `json:"stream_saver_display_name,omitempty"` + } +) func (m *manager) GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectLink, error) { ae := serializer.NewAggregateError() @@ -212,14 +218,14 @@ func (m *manager) GetUrlForRedirectedDirectLink(ctx context.Context, dl *ent.Dir return res, expire, nil } -func (m *manager) GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, opts ...fs.Option) ([]string, *time.Time, error) { +func (m *manager) GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, opts ...fs.Option) ([]EntityUrl, *time.Time, error) { o := newOption() for _, opt := range opts { opt.Apply(o) } var earliestExpireAt *time.Time - res := make([]string, len(args)) + res := make([]EntityUrl, len(args)) ae := serializer.NewAggregateError() for i, arg := range args { file, err := m.fs.Get( @@ -261,6 +267,12 @@ func (m *manager) GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, op m.l.Warning("Failed to execute navigator hooks: %s", err) } + policy, d, err := m.getEntityPolicyDriver(ctx, target, nil) + if err != nil { + ae.Add(arg.URI.String(), err) + continue + } + // Try to read from cache. cacheKey := entityUrlCacheKey(target.ID(), o.DownloadSpeed, getEntityDisplayName(file, target), o.IsDownload, m.settings.SiteURL(ctx).String()) @@ -270,17 +282,14 @@ func (m *manager) GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, op if cachedItem.ExpireAt != nil && (earliestExpireAt == nil || cachedItem.ExpireAt.Before(*earliestExpireAt)) { earliestExpireAt = cachedItem.ExpireAt } - res[i] = cachedItem.Url + res[i] = EntityUrl{ + Url: cachedItem.Url, + BrowserDownloadDisplayName: cachedItem.BrowserDownloadDisplayName, + } continue } // Cache miss, Generate new url - policy, d, err := m.getEntityPolicyDriver(ctx, target, nil) - if err != nil { - ae.Add(arg.URI.String(), err) - continue - } - source := entitysource.NewEntitySource(target, d, policy, m.auth, m.settings, m.hasher, m.dep.RequestClient(), m.l, m.config, m.dep.MimeDetector(ctx)) downloadUrl, err := source.Url(ctx, @@ -308,7 +317,12 @@ func (m *manager) GetEntityUrls(ctx context.Context, args []GetEntityUrlArgs, op }, cacheValidDuration) } - res[i] = downloadUrl.Url + res[i] = EntityUrl{ + Url: downloadUrl.Url, + } + if d.Capabilities().BrowserRelayedDownload { + res[i].BrowserDownloadDisplayName = getEntityDisplayName(file, target) + } } return res, earliestExpireAt, ae.Aggregate() diff --git a/pkg/filemanager/manager/operation.go b/pkg/filemanager/manager/operation.go index 1b70c9a..0df8951 100644 --- a/pkg/filemanager/manager/operation.go +++ b/pkg/filemanager/manager/operation.go @@ -36,8 +36,9 @@ type ( } EntityUrlCache struct { - Url string - ExpireAt *time.Time + Url string + BrowserDownloadDisplayName string + ExpireAt *time.Time } ) diff --git a/pkg/filemanager/workflows/remote_download.go b/pkg/filemanager/workflows/remote_download.go index 926b88f..7b06178 100644 --- a/pkg/filemanager/workflows/remote_download.go +++ b/pkg/filemanager/workflows/remote_download.go @@ -194,7 +194,7 @@ func (m *RemoteDownloadTask) createDownloadTask(ctx context.Context, dep depende return task.StatusError, fmt.Errorf("no torrent urls found") } - torrentUrl = torrentUrls[0] + torrentUrl = torrentUrls[0].Url } // Create download task diff --git a/service/explorer/file.go b/service/explorer/file.go index ca49db5..84fb754 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -367,8 +367,8 @@ type ( NoCache bool `json:"no_cache"` } FileURLResponse struct { - Urls []string `json:"urls"` - Expires *time.Time `json:"expires"` + Urls []manager.EntityUrl `json:"urls"` + Expires *time.Time `json:"expires"` } ArchiveDownloadSession struct { Uris []*fs.URI `json:"uris"` @@ -419,7 +419,7 @@ func (s *FileURLService) GetArchiveDownloadSession(c *gin.Context) (*FileURLResp } return &FileURLResponse{ - Urls: []string{finalUrl.String()}, + Urls: []manager.EntityUrl{{Url: finalUrl.String()}}, Expires: &expire, }, nil } @@ -473,7 +473,7 @@ func (s *FileURLService) Get(c *gin.Context) (*FileURLResponse, error) { //} if s.Redirect && len(uris) == 1 { - c.Redirect(http.StatusFound, res[0]) + c.Redirect(http.StatusFound, res[0].Url) return nil, nil } diff --git a/service/user/login.go b/service/user/login.go index 39d310b..4c8e19e 100644 --- a/service/user/login.go +++ b/service/user/login.go @@ -71,6 +71,7 @@ type ( // UserResetEmailService 发送密码重设邮件服务 UserResetEmailService struct { UserName string `form:"email" json:"email" binding:"required,email"` + Language string `form:"language" json:"language"` } UserResetEmailParameterCtx struct{} )