diff --git a/lib/rclone/api.ts b/lib/rclone/api.ts index 3538d65..f1a43ab 100644 --- a/lib/rclone/api.ts +++ b/lib/rclone/api.ts @@ -29,6 +29,7 @@ import { } from './requests' const RE_BACKSLASH = /\\/g +const RE_DASH = /-/g const RE_PATH_SEPARATOR = /[/\\]/ const RE_WINDOWS_EXTENDED_PATH = /(\/\/\?\/|\\\\\?\\)/ const RE_WINDOWS_DRIVE_LETTER = /^[a-zA-Z]:$/ @@ -410,13 +411,52 @@ async function startMountInner({ mountOptions.volname = `${sourcePath}-${Math.random().toString(36).substring(2, 3).toUpperCase()}` } + // Only genuinely global flags can ride the connection string (`global.` keys) — mount and + // VFS options go through the dedicated mountOpt/vfsOpt params below instead. Filter options + // have no per-mount channel at all (rclone builds the mount's VFS on a background context, + // so per-call filters never reach it) and are kept here as a no-op until upstream fixes it. const mergedOptions = { - ...mountOptions, ...(options.config || {}), - ...(options.vfs || {}), ...(options.filter || {}), } + const vfsOptions = { ...(options.vfs || {}) } + + // mountOpt/vfsOpt take JSON keyed by Go field names, so rekey the flag-name groups + // ("vfs_cache_mode" → "CacheMode") via the options/info registry before sending. Unknown + // keys pass through untouched — rclone ignores unrecognized fields. + const toStructOptions = ( + flags: Record, + infos: { Name: string; FieldName: string }[] | undefined + ) => { + const fieldNames = new Map((infos || []).map((info) => [info.Name, info.FieldName])) + return JSON.stringify( + Object.fromEntries( + Object.entries(flags).map(([key, value]) => [ + fieldNames.get(key.replace(RE_DASH, '_')) || key, + value, + ]) + ) + ) + } + + let structOptions: { mountOpt?: string; vfsOpt?: string } = {} + if (Object.keys(mountOptions).length > 0 || Object.keys(vfsOptions).length > 0) { + const optionsInfo = await pRetry( + async () => + await rclone('/options/info', { params: { query: { blocks: 'mount,vfs' } } }), + RETRY_OPTIONS + ) + structOptions = { + ...(Object.keys(mountOptions).length > 0 + ? { mountOpt: toStructOptions(mountOptions, optionsInfo?.mount) } + : {}), + ...(Object.keys(vfsOptions).length > 0 + ? { vfsOpt: toStructOptions(vfsOptions, optionsInfo?.vfs) } + : {}), + } + } + const { fullDirPath: srcFullDirPath, remoteName: srcRemoteName } = getFsInfo(source) const srcOptions = @@ -435,7 +475,8 @@ async function startMountInner({ remote: srcOptions, }), mountPoint: '*', - mount: 'nfsmount', + // No mountType — Windows uses rclone's default resolution (cmount/WinFsp) + ...structOptions, }, }, }), @@ -567,7 +608,8 @@ async function startMountInner({ } return mp })(), - mount: 'nfsmount', + ...(currentPlatform === 'macos' ? { mountType: 'nfsmount' } : {}), + ...structOptions, }, }, }), diff --git a/lib/rclone/mount.ts b/lib/rclone/mount.ts index 2b7d7aa..d9e8440 100644 --- a/lib/rclone/mount.ts +++ b/lib/rclone/mount.ts @@ -9,14 +9,7 @@ export async function needsMountPlugin() { console.log('[needsMountPlugin]') const currentPlatform = platform() - if (currentPlatform === 'macos') { - // check fuse-t or osxfuse - const hasFuseT = await exists('/Library/Application Support/fuse-t') - console.log('[needsMountPlugin] hasFuseT', hasFuseT) - const hasOsxFuse = await exists('/Library/Filesystems/macfuse.fs') - console.log('[needsMountPlugin] hasOsxFuse', hasOsxFuse) - return !hasFuseT && !hasOsxFuse - } + if (currentPlatform === 'windows') { // check winfsp const hasWinFsp = @@ -25,6 +18,7 @@ export async function needsMountPlugin() { console.log('[needsMountPlugin] hasWinFsp', hasWinFsp) return !hasWinFsp } + return false } @@ -32,26 +26,6 @@ export async function dialogGetMountPlugin() { console.log('[dialogGetMountPlugin]') const currentPlatform = platform() - if (currentPlatform === 'macos') { - // download fuse-t or osxfuse - const wantsDownload = await ask( - "Fuse-t is required on macOS to mount remotes. You can continue the operation once you're done with the installation.", - { - title: 'Fuse-t not installed', - kind: 'warning', - okLabel: 'Download', - cancelLabel: 'Cancel', - } - ) - if (wantsDownload) { - const fuseInstallerUrl = - 'https://github.com/macos-fuse-t/fuse-t/releases/download/1.0.49/fuse-t-macos-installer-1.0.49.pkg' - const localPath = `${await downloadDir()}/fuse-t-installer.pkg` - const installer = await (await fetch(fuseInstallerUrl)).arrayBuffer() - await writeFile(localPath, new Uint8Array(installer)) - await revealItemInDir(localPath) - } - } if (currentPlatform === 'windows') { // download winfsp const wantsDownload = await ask( diff --git a/src/pages/Mount.tsx b/src/pages/Mount.tsx index bb04982..f70bcb2 100644 --- a/src/pages/Mount.tsx +++ b/src/pages/Mount.tsx @@ -534,9 +534,9 @@ export default function Mount() {