mirror of https://github.com/cloudreve/Cloudreve
refactor(download): handle stream saver download outside of driver implementation (fix #2366)
parent
bdaf091aca
commit
0a28bf1689
|
@ -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
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ const (
|
|||
|
||||
const (
|
||||
QuerySearchName = "name"
|
||||
QuerySearchNameOpOr = "use_or"
|
||||
QuerySearchNameOpOr = "name_op_or"
|
||||
QuerySearchMetadataPrefix = "meta_"
|
||||
QuerySearchCaseFolding = "case_folding"
|
||||
QuerySearchType = "type"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -36,8 +36,9 @@ type (
|
|||
}
|
||||
|
||||
EntityUrlCache struct {
|
||||
Url string
|
||||
ExpireAt *time.Time
|
||||
Url string
|
||||
BrowserDownloadDisplayName string
|
||||
ExpireAt *time.Time
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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{}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue