From 24dc3293b21336fb683aa78c1ca62da8044826ac Mon Sep 17 00:00:00 2001 From: vishwajeet-13 Date: Mon, 7 Sep 2026 23:41:14 +0530 Subject: [PATCH 1/2] feat: download RAW/HEIC images as JPEG or PNG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download control on a RAW (.arw, .cr2, …) or HEIC/HEIF asset now offers Original / JPEG / PNG. JPEG/PNG are re-encoded server-side from the camera's embedded preview (RAW) or a libheif decode (HEIC), at full resolution, and streamed back with Content-Disposition: attachment. Other files download unchanged. - vms/image_export.py: format detection, render + encode, download handler - vms.api.download_converted_asset / vms.review_api.download_guest_converted_asset - useDownload().downloadConverted + isConvertibleStill mirror on the client - ReviewHeader and the asset action menu gain the format submenu - pillow-heif dependency for HEIC decode Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01N6kPRDGo6QbcVcc8DPkMTf --- .../src/components/assets/useAssetActions.ts | 43 +++++- .../src/components/review/ReviewHeader.vue | 55 +++++++- frontend/src/composables/useDownload.ts | 31 ++++- frontend/src/lib/fileType.ts | 12 ++ pyproject.toml | 1 + vms/api.py | 14 ++ vms/image_export.py | 131 ++++++++++++++++++ vms/review_api.py | 14 ++ vms/tests/test_image_export.py | 101 ++++++++++++++ 9 files changed, 387 insertions(+), 15 deletions(-) create mode 100644 vms/image_export.py create mode 100644 vms/tests/test_image_export.py diff --git a/frontend/src/components/assets/useAssetActions.ts b/frontend/src/components/assets/useAssetActions.ts index 2e22588..4a9778a 100644 --- a/frontend/src/components/assets/useAssetActions.ts +++ b/frontend/src/components/assets/useAssetActions.ts @@ -3,6 +3,7 @@ import { useRouter } from 'vue-router' import { dialog, toast, useCall, type DropdownOption } from 'frappe-ui' import type { Asset } from '@/types' import { serverMessage } from '@/lib/format' +import { isConvertibleStill } from '@/lib/fileType' import { useDownload } from '@/composables/useDownload' import { useVersionUpload } from '@/composables/useVersionUpload' @@ -50,7 +51,7 @@ interface AssetParams { */ export function useAssetActions(asset: Ref, ctx: AssetActionsContext): DropdownOption[] { const router = useRouter() - const { downloadOne } = useDownload() + const { downloadOne, downloadConverted } = useDownload() const { openVersionUpload } = useVersionUpload() const isReady = () => asset.value.status === 'Ready' @@ -187,6 +188,39 @@ export function useAssetActions(asset: Ref, ctx: AssetActionsContext): Dr }, ] + const downloadOption: DropdownOption = isConvertibleStill( + asset.value.file_type, + asset.value.file_name, + ) + ? { + label: 'Download', + icon: 'lucide-download', + condition: isReady, + submenu: [ + { + label: 'Original', + icon: 'lucide-download', + onClick: () => downloadOne(asset.value.name, asset.value.file_name), + }, + { + label: 'JPEG', + icon: 'lucide-image', + onClick: () => downloadConverted(asset.value.name, asset.value.file_name, 'jpeg'), + }, + { + label: 'PNG', + icon: 'lucide-image', + onClick: () => downloadConverted(asset.value.name, asset.value.file_name, 'png'), + }, + ], + } + : { + label: 'Download', + icon: 'lucide-download', + condition: isReady, + onClick: () => downloadOne(asset.value.name, asset.value.file_name), + } + return [ { label: 'Review', @@ -194,12 +228,7 @@ export function useAssetActions(asset: Ref, ctx: AssetActionsContext): Dr condition: isReady, onClick: () => router.push(`/review/${asset.value.name}`), }, - { - label: 'Download', - icon: 'lucide-download', - condition: isReady, - onClick: () => downloadOne(asset.value.name, asset.value.file_name), - }, + downloadOption, { label: 'Copy review link', icon: 'lucide-link', diff --git a/frontend/src/components/review/ReviewHeader.vue b/frontend/src/components/review/ReviewHeader.vue index 7245f8a..2348d60 100644 --- a/frontend/src/components/review/ReviewHeader.vue +++ b/frontend/src/components/review/ReviewHeader.vue @@ -18,8 +18,20 @@ /> + +