diff --git a/inventory/setting.go b/inventory/setting.go index 0879cd47..0b6a048a 100644 --- a/inventory/setting.go +++ b/inventory/setting.go @@ -26,7 +26,7 @@ type ( Get(ctx context.Context, name string) (string, error) // Set sets a setting value to DB. Set(ctx context.Context, settings map[string]string) error - // Gets gets multiple setting values from DB, returns error if any setting cannot be found. + // Gets gets multiple setting values from DB, returns a map of found settings; names with no matching setting are omitted. Gets(ctx context.Context, names []string) (map[string]string, error) } ) diff --git a/pkg/filemanager/driver/cos/media.go b/pkg/filemanager/driver/cos/media.go index 0a894a92..71750034 100644 --- a/pkg/filemanager/driver/cos/media.go +++ b/pkg/filemanager/driver/cos/media.go @@ -9,9 +9,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/mediameta" "github.com/cloudreve/Cloudreve/v4/pkg/request" "github.com/samber/lo" - "math" "net/http" - "strconv" "strings" "time" ) @@ -206,78 +204,25 @@ func (handler *Driver) extractMediaInfo(ctx context.Context, path string, opt *u } func parseGpsInfo(imageInfo ImageInfo) []driver.MediaMeta { - latitude := imageInfo["GPSLatitude"] // 31deg 16.26808' - longitude := imageInfo["GPSLongitude"] // 120deg 42.91039' - latRef := imageInfo["GPSLatitudeRef"] // North - lonRef := imageInfo["GPSLongitudeRef"] // East - - // Make sure all value exist in map - if latitude.Value == "" || longitude.Value == "" || latRef.Value == "" || lonRef.Value == "" { - return nil - } - - lat := parseRawGPS(latitude.Value, latRef.Value) - lon := parseRawGPS(longitude.Value, lonRef.Value) - if !math.IsNaN(lat) && !math.IsNaN(lon) { - lat, lng := mediameta.NormalizeGPS(lat, lon) - return []driver.MediaMeta{{ - Key: mediameta.GpsLat, - Value: fmt.Sprintf("%f", lat), - }, { - Key: mediameta.GpsLng, - Value: fmt.Sprintf("%f", lng), - }} - } - - return nil + return mediameta.GpsMeta(imageInfo["GPSLatitude"].Value, imageInfo["GPSLongitude"].Value, + imageInfo["GPSLatitudeRef"].Value, imageInfo["GPSLongitudeRef"].Value, parseRawGPS) } func parseRawGPS(gpsStr string, ref string) float64 { elem := strings.Split(gpsStr, " ") - if len(elem) < 1 { - return 0 - } - var ( - deg float64 - minutes float64 - seconds float64 - ) - - deg = getGpsElemValue(elem[0]) + var deg, minutes, seconds float64 + if len(elem) >= 1 { + deg = mediameta.GpsRationalElem(elem[0]) + } if len(elem) >= 2 { - minutes = getGpsElemValue(elem[1]) + minutes = mediameta.GpsRationalElem(elem[1]) } if len(elem) >= 3 { - seconds = getGpsElemValue(elem[2]) - } - - decimal := deg + minutes/60.0 + seconds/3600.0 - - if ref == "S" || ref == "W" { - return -decimal - } - - return decimal -} - -func getGpsElemValue(elm string) float64 { - elements := strings.Split(elm, "/") - if len(elements) != 2 { - return 0 - } - - numerator, err := strconv.ParseFloat(elements[0], 64) - if err != nil { - return 0 - } - - denominator, err := strconv.ParseFloat(elements[1], 64) - if err != nil || denominator == 0 { - return 0 + seconds = mediameta.GpsRationalElem(elem[2]) } - return numerator / denominator + return mediameta.DMSDecimal(deg, minutes, seconds, ref == "S" || ref == "W") } func handleCosError(resp string, originErr error) error { diff --git a/pkg/filemanager/driver/obs/media.go b/pkg/filemanager/driver/obs/media.go index fec894fd..596e27c9 100644 --- a/pkg/filemanager/driver/obs/media.go +++ b/pkg/filemanager/driver/obs/media.go @@ -4,9 +4,7 @@ import ( "context" "encoding/json" "fmt" - "math" "net/http" - "strconv" "strings" "time" @@ -62,76 +60,23 @@ func (d *Driver) MediaMeta(ctx context.Context, path, ext, language string) ([]d } func parseGpsInfo(imageInfo map[string]string) []driver.MediaMeta { - latitude := imageInfo["GPSLatitude"] // 31/1, 162680820/10000000, 0/1 - longitude := imageInfo["GPSLongitude"] // 120/1, 429103939/10000000, 0/1 - latRef := imageInfo["GPSLatitudeRef"] // N - lonRef := imageInfo["GPSLongitudeRef"] // E - - // Make sure all value exist in map - if latitude == "" || longitude == "" || latRef == "" || lonRef == "" { - return nil - } - - lat := parseRawGPS(latitude, latRef) - lon := parseRawGPS(longitude, lonRef) - if !math.IsNaN(lat) && !math.IsNaN(lon) { - lat, lng := mediameta.NormalizeGPS(lat, lon) - return []driver.MediaMeta{{ - Key: mediameta.GpsLat, - Value: fmt.Sprintf("%f", lat), - }, { - Key: mediameta.GpsLng, - Value: fmt.Sprintf("%f", lng), - }} - } - - return nil + return mediameta.GpsMeta(imageInfo["GPSLatitude"], imageInfo["GPSLongitude"], + imageInfo["GPSLatitudeRef"], imageInfo["GPSLongitudeRef"], parseRawGPS) } func parseRawGPS(gpsStr string, ref string) float64 { elem := strings.Split(gpsStr, ", ") - if len(elem) < 1 { - return 0 - } - - var ( - deg float64 - minutes float64 - seconds float64 - ) - deg = getGpsElemValue(elem[0]) + var deg, minutes, seconds float64 + if len(elem) >= 1 { + deg = mediameta.GpsRationalElem(elem[0]) + } if len(elem) >= 2 { - minutes = getGpsElemValue(elem[1]) + minutes = mediameta.GpsRationalElem(elem[1]) } if len(elem) >= 3 { - seconds = getGpsElemValue(elem[2]) - } - - decimal := deg + minutes/60.0 + seconds/3600.0 - - if ref == "S" || ref == "W" { - return -decimal - } - - return decimal -} - -func getGpsElemValue(elm string) float64 { - elements := strings.Split(elm, "/") - if len(elements) != 2 { - return 0 - } - - numerator, err := strconv.ParseFloat(elements[0], 64) - if err != nil { - return 0 - } - - denominator, err := strconv.ParseFloat(elements[1], 64) - if err != nil || denominator == 0 { - return 0 + seconds = mediameta.GpsRationalElem(elem[2]) } - return numerator / denominator + return mediameta.DMSDecimal(deg, minutes, seconds, ref == "S" || ref == "W") } diff --git a/pkg/filemanager/driver/oss/media.go b/pkg/filemanager/driver/oss/media.go index 11db9483..808ae12d 100644 --- a/pkg/filemanager/driver/oss/media.go +++ b/pkg/filemanager/driver/oss/media.go @@ -5,7 +5,6 @@ import ( "encoding/json" "encoding/xml" "fmt" - "math" "net/http" "strconv" "strings" @@ -292,45 +291,17 @@ func (handler *Driver) extractMediaInfo(ctx context.Context, path string, catego } func parseGpsInfo(imageInfo ImageInfo) []driver.MediaMeta { - latitude := imageInfo["GPSLatitude"] // 31deg 16.26808' - longitude := imageInfo["GPSLongitude"] // 120deg 42.91039' - latRef := imageInfo["GPSLatitudeRef"] // North - lonRef := imageInfo["GPSLongitudeRef"] // East - - // Make sure all value exist in map - if latitude.Value == "" || longitude.Value == "" || latRef.Value == "" || lonRef.Value == "" { - return nil - } - - lat := parseRawGPS(latitude.Value, latRef.Value) - lon := parseRawGPS(longitude.Value, lonRef.Value) - if !math.IsNaN(lat) && !math.IsNaN(lon) { - lat, lng := mediameta.NormalizeGPS(lat, lon) - return []driver.MediaMeta{{ - Key: mediameta.GpsLat, - Value: fmt.Sprintf("%f", lat), - }, { - Key: mediameta.GpsLng, - Value: fmt.Sprintf("%f", lng), - }} - } - - return nil + return mediameta.GpsMeta(imageInfo["GPSLatitude"].Value, imageInfo["GPSLongitude"].Value, + imageInfo["GPSLatitudeRef"].Value, imageInfo["GPSLongitudeRef"].Value, parseRawGPS) } func parseRawGPS(gpsStr string, ref string) float64 { elem := strings.Split(gpsStr, " ") - if len(elem) < 1 { - return 0 - } - var ( - deg float64 - minutes float64 - seconds float64 - ) - - deg, _ = strconv.ParseFloat(strings.TrimSuffix(elem[0], "deg"), 64) + var deg, minutes, seconds float64 + if len(elem) >= 1 { + deg, _ = strconv.ParseFloat(strings.TrimSuffix(elem[0], "deg"), 64) + } if len(elem) >= 2 { minutes, _ = strconv.ParseFloat(strings.TrimSuffix(elem[1], "'"), 64) } @@ -338,13 +309,7 @@ func parseRawGPS(gpsStr string, ref string) float64 { seconds, _ = strconv.ParseFloat(strings.TrimSuffix(elem[2], "\""), 64) } - decimal := deg + minutes/60.0 + seconds/3600.0 - - if ref == "South" || ref == "West" { - return -decimal - } - - return decimal + return mediameta.DMSDecimal(deg, minutes, seconds, ref == "South" || ref == "West") } func handleOssError(resp string, originErr error) error { diff --git a/pkg/filemanager/driver/qiniu/media.go b/pkg/filemanager/driver/qiniu/media.go index 81b106cc..5875a5be 100644 --- a/pkg/filemanager/driver/qiniu/media.go +++ b/pkg/filemanager/driver/qiniu/media.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math" "net/http" "net/url" "strconv" @@ -131,45 +130,17 @@ func unmarshalError(resp string, originErr error) error { } func parseGpsInfo(imageInfo ImageInfo) []driver.MediaMeta { - latitude := imageInfo["GPSLatitude"] // 31, 16.2680820, 0 - longitude := imageInfo["GPSLongitude"] // 120, 42.9103939, 0 - latRef := imageInfo["GPSLatitudeRef"] // N - lonRef := imageInfo["GPSLongitudeRef"] // E - - // Make sure all value exist in map - if latitude.Value == "" || longitude.Value == "" || latRef.Value == "" || lonRef.Value == "" { - return nil - } - - lat := parseRawGPS(latitude.Value, latRef.Value) - lon := parseRawGPS(longitude.Value, lonRef.Value) - if !math.IsNaN(lat) && !math.IsNaN(lon) { - lat, lng := mediameta.NormalizeGPS(lat, lon) - return []driver.MediaMeta{{ - Key: mediameta.GpsLat, - Value: fmt.Sprintf("%f", lat), - }, { - Key: mediameta.GpsLng, - Value: fmt.Sprintf("%f", lng), - }} - } - - return nil + return mediameta.GpsMeta(imageInfo["GPSLatitude"].Value, imageInfo["GPSLongitude"].Value, + imageInfo["GPSLatitudeRef"].Value, imageInfo["GPSLongitudeRef"].Value, parseRawGPS) } func parseRawGPS(gpsStr string, ref string) float64 { elem := strings.Split(gpsStr, ", ") - if len(elem) < 1 { - return 0 - } - - var ( - deg float64 - minutes float64 - seconds float64 - ) - deg, _ = strconv.ParseFloat(elem[0], 64) + var deg, minutes, seconds float64 + if len(elem) >= 1 { + deg, _ = strconv.ParseFloat(elem[0], 64) + } if len(elem) >= 2 { minutes, _ = strconv.ParseFloat(elem[1], 64) } @@ -177,11 +148,5 @@ func parseRawGPS(gpsStr string, ref string) float64 { seconds, _ = strconv.ParseFloat(elem[2], 64) } - decimal := deg + minutes/60.0 + seconds/3600.0 - - if ref == "S" || ref == "W" { - return -decimal - } - - return decimal + return mediameta.DMSDecimal(deg, minutes, seconds, ref == "S" || ref == "W") } diff --git a/pkg/filemanager/driver/upyun/media.go b/pkg/filemanager/driver/upyun/media.go index 743a3c72..7ec0971d 100644 --- a/pkg/filemanager/driver/upyun/media.go +++ b/pkg/filemanager/driver/upyun/media.go @@ -8,9 +8,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/mediameta" "github.com/cloudreve/Cloudreve/v4/pkg/request" "github.com/samber/lo" - "math" "net/http" - "strconv" "strings" "time" ) @@ -77,76 +75,23 @@ func unmarshalError(resp string, err error) error { } func parseGpsInfo(imageInfo map[string]string) []driver.MediaMeta { - latitude := imageInfo["GPSLatitude"] // 31/1, 162680820/10000000, 0/1 - longitude := imageInfo["GPSLongitude"] // 120/1, 429103939/10000000, 0/1 - latRef := imageInfo["GPSLatitudeRef"] // N - lonRef := imageInfo["GPSLongitudeRef"] // E - - // Make sure all value exist in map - if latitude == "" || longitude == "" || latRef == "" || lonRef == "" { - return nil - } - - lat := parseRawGPS(latitude, latRef) - lon := parseRawGPS(longitude, lonRef) - if !math.IsNaN(lat) && !math.IsNaN(lon) { - lat, lng := mediameta.NormalizeGPS(lat, lon) - return []driver.MediaMeta{{ - Key: mediameta.GpsLat, - Value: fmt.Sprintf("%f", lat), - }, { - Key: mediameta.GpsLng, - Value: fmt.Sprintf("%f", lng), - }} - } - - return nil + return mediameta.GpsMeta(imageInfo["GPSLatitude"], imageInfo["GPSLongitude"], + imageInfo["GPSLatitudeRef"], imageInfo["GPSLongitudeRef"], parseRawGPS) } func parseRawGPS(gpsStr string, ref string) float64 { elem := strings.Split(gpsStr, ",") - if len(elem) < 1 { - return 0 - } - var ( - deg float64 - minutes float64 - seconds float64 - ) - - deg = getGpsElemValue(elem[0]) + var deg, minutes, seconds float64 + if len(elem) >= 1 { + deg = mediameta.GpsRationalElem(elem[0]) + } if len(elem) >= 2 { - minutes = getGpsElemValue(elem[1]) + minutes = mediameta.GpsRationalElem(elem[1]) } if len(elem) >= 3 { - seconds = getGpsElemValue(elem[2]) - } - - decimal := deg + minutes/60.0 + seconds/3600.0 - - if ref == "S" || ref == "W" { - return -decimal - } - - return decimal -} - -func getGpsElemValue(elm string) float64 { - elements := strings.Split(elm, "/") - if len(elements) != 2 { - return 0 - } - - numerator, err := strconv.ParseFloat(elements[0], 64) - if err != nil { - return 0 - } - - denominator, err := strconv.ParseFloat(elements[1], 64) - if err != nil || denominator == 0 { - return 0 + seconds = mediameta.GpsRationalElem(elem[2]) } - return numerator / denominator + return mediameta.DMSDecimal(deg, minutes, seconds, ref == "S" || ref == "W") } diff --git a/pkg/filemanager/fs/dbfs/dbfs.go b/pkg/filemanager/fs/dbfs/dbfs.go index 4750aaed..b91a6c22 100644 --- a/pkg/filemanager/fs/dbfs/dbfs.go +++ b/pkg/filemanager/fs/dbfs/dbfs.go @@ -93,26 +93,41 @@ func (f *DBFS) writePermitted(file *File, capability NavigatorCapability) bool { return caps != nil && caps.Enabled(int(capability)) } -func NewDatabaseFS(u *ent.User, fileClient inventory.FileClient, shareClient inventory.ShareClient, - l logging.Logger, ls lock.LockSystem, settingClient setting.Provider, - storagePolicyClient inventory.StoragePolicyClient, hasher hashid.Encoder, userClient inventory.UserClient, - cache, stateKv cache.Driver, directLinkClient inventory.DirectLinkClient, encryptorFactory encrypt.CryptorFactory, eventHub eventhub.EventHub) fs.FileSystem { +// DBFSDependencies groups the collaborators wired into a DBFS instance so the +// constructor call site names each dependency. +type DBFSDependencies struct { + FileClient inventory.FileClient + ShareClient inventory.ShareClient + UserClient inventory.UserClient + StoragePolicyClient inventory.StoragePolicyClient + DirectLinkClient inventory.DirectLinkClient + Logger logging.Logger + LockSystem lock.LockSystem + SettingProvider setting.Provider + Hasher hashid.Encoder + Cache cache.Driver + StateKV cache.Driver + EncryptorFactory encrypt.CryptorFactory + EventHub eventhub.EventHub +} + +func NewDatabaseFS(u *ent.User, deps DBFSDependencies) fs.FileSystem { return &DBFS{ user: u, navigators: make(map[string]Navigator), - fileClient: fileClient, - shareClient: shareClient, - l: l, - ls: ls, - settingClient: settingClient, - storagePolicyClient: storagePolicyClient, - hasher: hasher, - userClient: userClient, - cache: cache, - stateKv: stateKv, - directLinkClient: directLinkClient, - encryptorFactory: encryptorFactory, - eventHub: eventHub, + fileClient: deps.FileClient, + shareClient: deps.ShareClient, + l: deps.Logger, + ls: deps.LockSystem, + settingClient: deps.SettingProvider, + storagePolicyClient: deps.StoragePolicyClient, + hasher: deps.Hasher, + userClient: deps.UserClient, + cache: deps.Cache, + stateKv: deps.StateKV, + directLinkClient: deps.DirectLinkClient, + encryptorFactory: deps.EncryptorFactory, + eventHub: deps.EventHub, } } diff --git a/pkg/filemanager/fs/dbfs/my_navigator.go b/pkg/filemanager/fs/dbfs/my_navigator.go index b607ba21..c69081b0 100644 --- a/pkg/filemanager/fs/dbfs/my_navigator.go +++ b/pkg/filemanager/fs/dbfs/my_navigator.go @@ -153,27 +153,24 @@ func (n *myNavigator) Walk(ctx context.Context, levelFiles []*File, limit, depth } func (n *myNavigator) FollowTx(ctx context.Context) (func(), error) { - if _, ok := ctx.Value(inventory.TxCtx{}).(*inventory.Tx); !ok { - return nil, fmt.Errorf("navigator: no inherited transaction found in context") - } - newFileClient, _, _, err := inventory.WithTx(ctx, n.fileClient) + oldBase := n.baseNavigator.fileClient + revertFile, err := followTxClients(ctx, &n.fileClient) if err != nil { return nil, err } - newUserClient, _, _, err := inventory.WithTx(ctx, n.userClient) - - oldFileClient, oldUserClient := n.fileClient, n.userClient - revert := func() { - n.fileClient = oldFileClient - n.userClient = oldUserClient - n.baseNavigator.fileClient = oldFileClient + revertUser, err := followTxClients(ctx, &n.userClient) + if err != nil { + revertFile() + return nil, err } - n.fileClient = newFileClient - n.userClient = newUserClient - n.baseNavigator.fileClient = newFileClient - return revert, nil + n.baseNavigator.fileClient = n.fileClient + return func() { + revertUser() + revertFile() + n.baseNavigator.fileClient = oldBase + }, nil } func (n *myNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, file *File) error { diff --git a/pkg/filemanager/fs/dbfs/navigator.go b/pkg/filemanager/fs/dbfs/navigator.go index c2ac0be1..64009df3 100644 --- a/pkg/filemanager/fs/dbfs/navigator.go +++ b/pkg/filemanager/fs/dbfs/navigator.go @@ -19,6 +19,31 @@ import ( "github.com/samber/lo" ) +// followTxClients swaps each client field to its tx-bound instance from the +// transaction in ctx, returning a revert closure that restores the originals. +func followTxClients[T inventory.TxOperator](ctx context.Context, fields ...*T) (func(), error) { + if _, ok := ctx.Value(inventory.TxCtx{}).(*inventory.Tx); !ok { + return nil, fmt.Errorf("navigator: no inherited transaction found in context") + } + + olds := make([]T, len(fields)) + for i, f := range fields { + newClient, _, _, err := inventory.WithTx(ctx, *f) + if err != nil { + return nil, err + } + + olds[i] = *f + *f = newClient + } + + return func() { + for i, f := range fields { + *f = olds[i] + } + }, nil +} + var ( ErrFsNotInitialized = fmt.Errorf("fs not initialized") ErrPermissionDenied = serializer.NewError(serializer.CodeNoPermissionErr, "Permission denied", nil) diff --git a/pkg/filemanager/fs/dbfs/share_navigator.go b/pkg/filemanager/fs/dbfs/share_navigator.go index 0eb36de6..85f69362 100644 --- a/pkg/filemanager/fs/dbfs/share_navigator.go +++ b/pkg/filemanager/fs/dbfs/share_navigator.go @@ -351,27 +351,24 @@ func (n *shareNavigator) shareCapabilities() *boolset.BooleanSet { } func (n *shareNavigator) FollowTx(ctx context.Context) (func(), error) { - if _, ok := ctx.Value(inventory.TxCtx{}).(*inventory.Tx); !ok { - return nil, fmt.Errorf("navigator: no inherited transaction found in context") - } - newFileClient, _, _, err := inventory.WithTx(ctx, n.fileClient) + oldBase := n.baseNavigator.fileClient + revertFile, err := followTxClients(ctx, &n.fileClient) if err != nil { return nil, err } - newSharClient, _, _, err := inventory.WithTx(ctx, n.shareClient) - - oldFileClient, oldShareClient := n.fileClient, n.shareClient - revert := func() { - n.fileClient = oldFileClient - n.shareClient = oldShareClient - n.baseNavigator.fileClient = oldFileClient + revertShare, err := followTxClients(ctx, &n.shareClient) + if err != nil { + revertFile() + return nil, err } - n.fileClient = newFileClient - n.shareClient = newSharClient - n.baseNavigator.fileClient = newFileClient - return revert, nil + n.baseNavigator.fileClient = n.fileClient + return func() { + revertShare() + revertFile() + n.baseNavigator.fileClient = oldBase + }, nil } func (n *shareNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, file *File) error { diff --git a/pkg/filemanager/fs/dbfs/sharewithme_navigator.go b/pkg/filemanager/fs/dbfs/sharewithme_navigator.go index 6c42a619..5c3fa40f 100644 --- a/pkg/filemanager/fs/dbfs/sharewithme_navigator.go +++ b/pkg/filemanager/fs/dbfs/sharewithme_navigator.go @@ -120,23 +120,17 @@ func (t *sharedWithMeNavigator) Walk(ctx context.Context, levelFiles []*File, li } func (n *sharedWithMeNavigator) FollowTx(ctx context.Context) (func(), error) { - if _, ok := ctx.Value(inventory.TxCtx{}).(*inventory.Tx); !ok { - return nil, fmt.Errorf("navigator: no inherited transaction found in context") - } - newFileClient, _, _, err := inventory.WithTx(ctx, n.fileClient) + oldBase := n.baseNavigator.fileClient + revert, err := followTxClients(ctx, &n.fileClient) if err != nil { return nil, err } - oldFileClient := n.fileClient - revert := func() { - n.fileClient = oldFileClient - n.baseNavigator.fileClient = oldFileClient - } - - n.fileClient = newFileClient - n.baseNavigator.fileClient = newFileClient - return revert, nil + n.baseNavigator.fileClient = n.fileClient + return func() { + revert() + n.baseNavigator.fileClient = oldBase + }, nil } func (n *sharedWithMeNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, file *File) error { diff --git a/pkg/filemanager/fs/dbfs/trash_navigator.go b/pkg/filemanager/fs/dbfs/trash_navigator.go index ede265ed..af792098 100644 --- a/pkg/filemanager/fs/dbfs/trash_navigator.go +++ b/pkg/filemanager/fs/dbfs/trash_navigator.go @@ -135,23 +135,17 @@ func (t *trashNavigator) Walk(ctx context.Context, levelFiles []*File, limit, de } func (n *trashNavigator) FollowTx(ctx context.Context) (func(), error) { - if _, ok := ctx.Value(inventory.TxCtx{}).(*inventory.Tx); !ok { - return nil, fmt.Errorf("navigator: no inherited transaction found in context") - } - newFileClient, _, _, err := inventory.WithTx(ctx, n.fileClient) + oldBase := n.baseNavigator.fileClient + revert, err := followTxClients(ctx, &n.fileClient) if err != nil { return nil, err } - oldFileClient := n.fileClient - revert := func() { - n.fileClient = oldFileClient - n.baseNavigator.fileClient = oldFileClient - } - - n.fileClient = newFileClient - n.baseNavigator.fileClient = newFileClient - return revert, nil + n.baseNavigator.fileClient = n.fileClient + return func() { + revert() + n.baseNavigator.fileClient = oldBase + }, nil } func (n *trashNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, file *File) error { diff --git a/pkg/filemanager/manager/manager.go b/pkg/filemanager/manager/manager.go index 1352bea5..39a39085 100644 --- a/pkg/filemanager/manager/manager.go +++ b/pkg/filemanager/manager/manager.go @@ -165,9 +165,21 @@ func NewFileManager(dep dependency.Dep, u *ent.User) FileManager { l: dep.Logger(), user: u, settings: dep.SettingProvider(), - fs: dbfs.NewDatabaseFS(u, dep.FileClient(), dep.ShareClient(), dep.Logger(), dep.LockSystem(), - dep.SettingProvider(), dep.StoragePolicyClient(), dep.HashIDEncoder(), dep.UserClient(), dep.KV(), dep.NavigatorStateKV(), - dep.DirectLinkClient(), dep.EncryptorFactory(context.TODO()), dep.EventHub()), + fs: dbfs.NewDatabaseFS(u, dbfs.DBFSDependencies{ + FileClient: dep.FileClient(), + ShareClient: dep.ShareClient(), + UserClient: dep.UserClient(), + StoragePolicyClient: dep.StoragePolicyClient(), + DirectLinkClient: dep.DirectLinkClient(), + Logger: dep.Logger(), + LockSystem: dep.LockSystem(), + SettingProvider: dep.SettingProvider(), + Hasher: dep.HashIDEncoder(), + Cache: dep.KV(), + StateKV: dep.NavigatorStateKV(), + EncryptorFactory: dep.EncryptorFactory(context.TODO()), + EventHub: dep.EventHub(), + }), kv: dep.KV(), config: config, auth: dep.GeneralAuth(), diff --git a/pkg/mediameta/gps.go b/pkg/mediameta/gps.go new file mode 100644 index 00000000..a2fb1b6f --- /dev/null +++ b/pkg/mediameta/gps.go @@ -0,0 +1,65 @@ +package mediameta + +import ( + "fmt" + "math" + "strconv" + "strings" + + "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/driver" +) + +// DMSDecimal converts degree/minute/second GPS components to a signed +// decimal degree; negative flips the sign for south/west references. +func DMSDecimal(deg, minutes, seconds float64, negative bool) float64 { + decimal := deg + minutes/60.0 + seconds/3600.0 + if negative { + return -decimal + } + return decimal +} + +// GpsRationalElem parses one rational GPS element of the form "n/d" into a +// decimal value. Returns 0 for malformed or zero-denominator elements. +func GpsRationalElem(elm string) float64 { + elements := strings.Split(elm, "/") + if len(elements) != 2 { + return 0 + } + + numerator, err := strconv.ParseFloat(elements[0], 64) + if err != nil { + return 0 + } + + denominator, err := strconv.ParseFloat(elements[1], 64) + if err != nil || denominator == 0 { + return 0 + } + + return numerator / denominator +} + +// GpsMeta builds the latitude/longitude MediaMeta pair from raw EXIF parts. +// parse converts a raw coordinate string and its hemisphere reference into +// decimal degrees; drivers pass their vendor-specific parser. +func GpsMeta(latitude, longitude, latRef, lonRef string, parse func(raw, ref string) float64) []driver.MediaMeta { + if latitude == "" || longitude == "" || latRef == "" || lonRef == "" { + return nil + } + + lat := parse(latitude, latRef) + lon := parse(longitude, lonRef) + if math.IsNaN(lat) || math.IsNaN(lon) { + return nil + } + + latN, lngN := NormalizeGPS(lat, lon) + return []driver.MediaMeta{{ + Key: GpsLat, + Value: fmt.Sprintf("%f", latN), + }, { + Key: GpsLng, + Value: fmt.Sprintf("%f", lngN), + }} +}