diff --git a/src/composables/useDownload.ts b/src/composables/useDownload.ts index 22d9d3a424..87191dca07 100644 --- a/src/composables/useDownload.ts +++ b/src/composables/useDownload.ts @@ -2,10 +2,11 @@ import { whenever } from '@vueuse/core' import { onMounted, ref } from 'vue' import { useCivitaiModel } from '@/composables/useCivitaiModel' -import { isCivitaiModelUrl } from '@/utils/formatUtil' +import { downloadUrlToHfRepoUrl, isCivitaiModelUrl } from '@/utils/formatUtil' export function useDownload(url: string, fileName?: string) { const fileSize = ref(null) + const error = ref(null) const setFileSize = (size: number) => { fileSize.value = size @@ -25,6 +26,7 @@ export function useDownload(url: string, fileName?: string) { } } catch (e) { console.error('Error fetching file size:', e) + error.value = e instanceof Error ? e : new Error(String(e)) return null } } @@ -34,8 +36,13 @@ export function useDownload(url: string, fileName?: string) { */ const triggerBrowserDownload = () => { const link = document.createElement('a') - link.href = url - link.download = fileName || url.split('/').pop() || 'download' + if (url.includes('huggingface.co') && error.value) { + // If model is a gated HF model, send user to the repo page so they can sign in first + link.href = downloadUrlToHfRepoUrl(url) + } else { + link.href = url + link.download = fileName || url.split('/').pop() || 'download' + } link.target = '_blank' // Opens in new tab if download attribute is not supported link.rel = 'noopener noreferrer' // Security best practice for _blank links link.click() diff --git a/src/utils/formatUtil.ts b/src/utils/formatUtil.ts index 5c3653b04d..1f35d92411 100644 --- a/src/utils/formatUtil.ts +++ b/src/utils/formatUtil.ts @@ -357,3 +357,30 @@ export const isCivitaiModelUrl = (url: string): boolean => { /^\/api\/v1\/models-versions\/(\d+)$/.test(pathname) ) } + +/** + * Converts a Hugging Face download URL to a repository page URL + * @param url The download URL to convert + * @returns The repository page URL or the original URL if conversion fails + * @example + * downloadUrlToHfRepoUrl( + * 'https://huggingface.co/bfl/FLUX.1/resolve/main/flux1-canny-dev.safetensors?download=true' + * ) // https://huggingface.co/bfl/FLUX.1 + */ +export const downloadUrlToHfRepoUrl = (url: string): string => { + try { + const urlObj = new URL(url) + const pathname = urlObj.pathname + + // Use regex to match everything before /resolve/ or /blob/ + const regex = /^(.*?)(?:\/resolve\/|\/blob\/|$)/ + const repoPathMatch = regex.exec(pathname) + + // Extract the repository path and remove leading slash if present + const repoPath = repoPathMatch?.[1]?.replace(/^\//, '') || '' + + return `https://huggingface.co/${repoPath}` + } catch (error) { + return url + } +}