From d05e4eac58aa930cbb398da045dfc28e3b805c5c Mon Sep 17 00:00:00 2001 From: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com> Date: Fri, 6 Feb 2026 05:06:11 +0100 Subject: [PATCH] fix: include subfolder in asset download URL for audio/video files (#8684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - `getAssetUrl()` was constructing `/view` URLs without the `subfolder` query parameter, causing backend to return "file not found" for assets stored in subfolders (common for audio/video outputs) - Preview/playback was unaffected because it uses `preview_url` from `ResultItemImpl.url` which correctly includes `subfolder` - Fixed `getAssetUrl()` to include `subfolder` from `asset.user_metadata` when present - Simplified download functions to prefer `preview_url` (already correct) with `getAssetUrl` as fallback ## Test plan - [ ] Generate audio/video output (e.g. via SaveAudio node) that saves to a subfolder - [ ] Right-click the asset in the assets sidebar and click Download — should download successfully - [ ] Select multiple audio/video assets and use bulk download — should download all - [ ] Verify image downloads still work as before - [ ] Verify cloud environment downloads still work (uses `preview_url` path) ## Summary by CodeRabbit ## Release Notes * **New Features** * Added support for organizing and downloading assets from subfolders. * **Refactor** * Improved asset URL generation and download handling for better reliability and performance. --- .../composables/useMediaAssetActions.ts | 22 ++++--------------- src/platform/assets/utils/assetUrlUtil.ts | 13 +++++++---- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/platform/assets/composables/useMediaAssetActions.ts b/src/platform/assets/composables/useMediaAssetActions.ts index b5b06ece3..c4e3b79a4 100644 --- a/src/platform/assets/composables/useMediaAssetActions.ts +++ b/src/platform/assets/composables/useMediaAssetActions.ts @@ -65,15 +65,8 @@ export function useMediaAssetActions() { try { const filename = targetAsset.name - let downloadUrl: string - - // In cloud, use preview_url directly (from cloud storage) - // In OSS/localhost, use the /view endpoint - if (isCloud && targetAsset.preview_url) { - downloadUrl = targetAsset.preview_url - } else { - downloadUrl = getAssetUrl(targetAsset) - } + // Prefer preview_url (already includes subfolder) with getAssetUrl as fallback + const downloadUrl = targetAsset.preview_url || getAssetUrl(targetAsset) downloadFile(downloadUrl, filename) @@ -103,15 +96,8 @@ export function useMediaAssetActions() { try { assets.forEach((asset) => { const filename = asset.name - let downloadUrl: string - - // In cloud, use preview_url directly (from GCS or other cloud storage) - // In OSS/localhost, use the /view endpoint - if (isCloud && asset.preview_url) { - downloadUrl = asset.preview_url - } else { - downloadUrl = getAssetUrl(asset) - } + // Prefer preview_url (already includes subfolder) with getAssetUrl as fallback + const downloadUrl = asset.preview_url || getAssetUrl(asset) downloadFile(downloadUrl, filename) }) diff --git a/src/platform/assets/utils/assetUrlUtil.ts b/src/platform/assets/utils/assetUrlUtil.ts index 60956eb5f..ad0c2be25 100644 --- a/src/platform/assets/utils/assetUrlUtil.ts +++ b/src/platform/assets/utils/assetUrlUtil.ts @@ -8,7 +8,7 @@ import { getAssetType } from './assetTypeUtil' /** * Get the download/view URL for an asset - * Constructs the proper URL with filename encoding and type parameter + * Constructs the proper URL with filename encoding, type, and subfolder parameters * * @param asset The asset to get URL for * @param defaultType Default type if asset doesn't have tags (default: 'output') @@ -23,7 +23,12 @@ export function getAssetUrl( defaultType: 'input' | 'output' = 'output' ): string { const assetType = getAssetType(asset, defaultType) - return api.apiURL( - `/view?filename=${encodeURIComponent(asset.name)}&type=${assetType}` - ) + const subfolder = asset.user_metadata?.subfolder + const params = new URLSearchParams() + params.set('filename', asset.name) + params.set('type', assetType) + if (typeof subfolder === 'string' && subfolder) { + params.set('subfolder', subfolder) + } + return api.apiURL(`/view?${params}`) }