mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-23 06:10:32 +00:00
[backport cloud/1.43] feat: add civitai.red hostname support (#12090)
Backport of #11349 to cloud/1.43. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12090-backport-cloud-1-43-feat-add-civitai-red-hostname-support-35a6d73d36508141b2dfe6d6985f2959) by [Unito](https://www.unito.io) Co-authored-by: Hunter <huntcsg@users.noreply.github.com> Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Terry Jia <terryjia88@gmail.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
getMediaTypeFromFilename,
|
||||
getPathDetails,
|
||||
highlightQuery,
|
||||
isCivitaiModelUrl,
|
||||
isPreviewableMediaType,
|
||||
truncateFilename
|
||||
} from './formatUtil'
|
||||
@@ -357,4 +358,12 @@ describe('formatUtil', () => {
|
||||
expect(isPreviewableMediaType('other')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isCivitaiModelUrl', () => {
|
||||
it('recognizes civitai.red model URLs', () => {
|
||||
expect(
|
||||
isCivitaiModelUrl('https://civitai.red/api/download/models/123456')
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -361,9 +361,17 @@ export const generateUUID = (): string => {
|
||||
*/
|
||||
export const isCivitaiModelUrl = (url: string): boolean => {
|
||||
if (!isValidUrl(url)) return false
|
||||
if (!url.includes('civitai.com')) return false
|
||||
|
||||
const urlObj = new URL(url)
|
||||
const hostname = urlObj.hostname.toLowerCase()
|
||||
const isCivitaiHost =
|
||||
hostname === 'civitai.com' ||
|
||||
hostname.endsWith('.civitai.com') ||
|
||||
hostname === 'civitai.red' ||
|
||||
hostname.endsWith('.civitai.red')
|
||||
if (!isCivitaiHost) {
|
||||
return false
|
||||
}
|
||||
const pathname = urlObj.pathname
|
||||
|
||||
return (
|
||||
|
||||
@@ -17,7 +17,7 @@ vi.mock('@/platform/assets/services/assetService', () => ({
|
||||
vi.mock('@/platform/assets/importSources/civitaiImportSource', () => ({
|
||||
civitaiImportSource: {
|
||||
name: 'Civitai',
|
||||
hostnames: ['civitai.com'],
|
||||
hostnames: ['civitai.com', 'civitai.red'],
|
||||
fetchMetadata: vi.fn()
|
||||
}
|
||||
}))
|
||||
@@ -154,4 +154,28 @@ describe('useUploadModelWizard', () => {
|
||||
expect(wizard.uploadStatus.value).toBe('error')
|
||||
expect(wizard.uploadError.value).toBe('Network error')
|
||||
})
|
||||
|
||||
it('accepts civitai.red model URLs', async () => {
|
||||
const { assetService } =
|
||||
await import('@/platform/assets/services/assetService')
|
||||
|
||||
const asyncResponse: AsyncUploadResponse = {
|
||||
type: 'async',
|
||||
task: {
|
||||
task_id: 'task-red',
|
||||
status: 'created',
|
||||
message: 'Download queued'
|
||||
}
|
||||
}
|
||||
vi.mocked(assetService.uploadAssetAsync).mockResolvedValue(asyncResponse)
|
||||
|
||||
const wizard = useUploadModelWizard(modelTypes)
|
||||
wizard.wizardData.value.url = 'https://civitai.red/models/12345'
|
||||
wizard.selectedModelType.value = 'checkpoints'
|
||||
|
||||
await wizard.uploadModel()
|
||||
|
||||
expect(assetService.uploadAssetAsync).toHaveBeenCalled()
|
||||
expect(wizard.uploadStatus.value).toBe('processing')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,5 +6,5 @@ import type { ImportSource } from '@/platform/assets/types/importSource'
|
||||
export const civitaiImportSource: ImportSource = {
|
||||
type: 'civitai',
|
||||
name: 'Civitai',
|
||||
hostnames: ['civitai.com']
|
||||
hostnames: ['civitai.com', 'civitai.red']
|
||||
}
|
||||
|
||||
@@ -187,6 +187,11 @@ describe('assetMetadataUtils', () => {
|
||||
url: 'https://civitai.com/models/123',
|
||||
expected: 'Civitai'
|
||||
},
|
||||
{
|
||||
name: 'returns Civitai for civitai.red',
|
||||
url: 'https://civitai.red/models/123',
|
||||
expected: 'Civitai'
|
||||
},
|
||||
{
|
||||
name: 'returns Hugging Face for huggingface.co',
|
||||
url: 'https://huggingface.co/org/model',
|
||||
|
||||
@@ -126,8 +126,22 @@ export function getAssetAdditionalTags(asset: AssetItem): string[] {
|
||||
* @returns Human-readable source name
|
||||
*/
|
||||
export function getSourceName(url: string): string {
|
||||
if (url.includes('civitai.com')) return 'Civitai'
|
||||
if (url.includes('huggingface.co')) return 'Hugging Face'
|
||||
try {
|
||||
const hostname = new URL(url).hostname.toLowerCase()
|
||||
if (
|
||||
hostname === 'civitai.com' ||
|
||||
hostname.endsWith('.civitai.com') ||
|
||||
hostname === 'civitai.red' ||
|
||||
hostname.endsWith('.civitai.red')
|
||||
) {
|
||||
return 'Civitai'
|
||||
}
|
||||
if (hostname === 'huggingface.co' || hostname.endsWith('.huggingface.co')) {
|
||||
return 'Hugging Face'
|
||||
}
|
||||
} catch {
|
||||
// fall through for invalid URLs
|
||||
}
|
||||
return 'Source'
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ vi.mock('@/platform/assets/importSources/civitaiImportSource', () => ({
|
||||
civitaiImportSource: {
|
||||
type: 'civitai',
|
||||
name: 'Civitai',
|
||||
hostnames: ['civitai.com']
|
||||
hostnames: ['civitai.com', 'civitai.red']
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||||
|
||||
import { fetchModelMetadata, toBrowsableUrl } from './missingModelDownload'
|
||||
import {
|
||||
fetchModelMetadata,
|
||||
isModelDownloadable,
|
||||
toBrowsableUrl
|
||||
} from './missingModelDownload'
|
||||
|
||||
const fetchMock = vi.fn()
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
@@ -177,4 +181,35 @@ describe('toBrowsableUrl', () => {
|
||||
'https://civitai.com/models/12345'
|
||||
)
|
||||
})
|
||||
|
||||
it('converts civitai.red URLs to model pages', () => {
|
||||
expect(
|
||||
toBrowsableUrl('https://civitai.red/api/download/models/12345')
|
||||
).toBe('https://civitai.red/models/12345')
|
||||
expect(toBrowsableUrl('https://civitai.red/api/v1/models/12345')).toBe(
|
||||
'https://civitai.red/models/12345'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isModelDownloadable', () => {
|
||||
it('allows civitai.red URLs', () => {
|
||||
expect(
|
||||
isModelDownloadable({
|
||||
name: 'model.safetensors',
|
||||
url: 'https://civitai.red/api/download/models/12345',
|
||||
directory: 'checkpoints'
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects non-allowlisted URLs', () => {
|
||||
expect(
|
||||
isModelDownloadable({
|
||||
name: 'model.safetensors',
|
||||
url: 'https://example.com/model.safetensors',
|
||||
directory: 'checkpoints'
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useElectronDownloadStore } from '@/stores/electronDownloadStore'
|
||||
|
||||
const ALLOWED_SOURCES = [
|
||||
'https://civitai.com/',
|
||||
'https://civitai.red/',
|
||||
'https://huggingface.co/',
|
||||
'http://localhost:'
|
||||
] as const
|
||||
|
||||
Reference in New Issue
Block a user