|
31 | 31 | :can-fetch-metadata="canFetchMetadata" |
32 | 32 | :can-upload-model="canUploadModel" |
33 | 33 | :upload-status="uploadStatus" |
34 | | - :on-back="goToPreviousStep" |
35 | | - :on-fetch-metadata="handleFetchMetadata" |
36 | | - :on-upload="handleUploadModel" |
37 | | - :on-close="handleClose" |
| 34 | + @back="goToPreviousStep" |
| 35 | + @fetch-metadata="handleFetchMetadata" |
| 36 | + @upload="handleUploadModel" |
| 37 | + @close="handleClose" |
38 | 38 | /> |
39 | 39 | </div> |
40 | 40 | </template> |
41 | 41 |
|
42 | 42 | <script setup lang="ts"> |
43 | | -import { computed, onMounted, ref, watch } from 'vue' |
| 43 | +import { onMounted } from 'vue' |
44 | 44 |
|
45 | | -import { st } from '@/i18n' |
46 | 45 | import UploadModelConfirmation from '@/platform/assets/components/UploadModelConfirmation.vue' |
47 | 46 | import UploadModelFooter from '@/platform/assets/components/UploadModelFooter.vue' |
48 | 47 | import UploadModelProgress from '@/platform/assets/components/UploadModelProgress.vue' |
49 | 48 | import UploadModelUrlInput from '@/platform/assets/components/UploadModelUrlInput.vue' |
50 | 49 | import { useModelTypes } from '@/platform/assets/composables/useModelTypes' |
51 | | -import type { AssetMetadata } from '@/platform/assets/schemas/assetSchema' |
52 | | -import { assetService } from '@/platform/assets/services/assetService' |
| 50 | +import { useUploadModelWizard } from '@/platform/assets/composables/useUploadModelWizard' |
53 | 51 | import { useDialogStore } from '@/stores/dialogStore' |
54 | 52 |
|
55 | 53 | const dialogStore = useDialogStore() |
| 54 | +const { modelTypes, fetchModelTypes } = useModelTypes() |
56 | 55 |
|
57 | 56 | const emit = defineEmits<{ |
58 | 57 | 'upload-success': [] |
59 | 58 | }>() |
60 | 59 |
|
61 | | -const currentStep = ref(1) |
62 | | -const isFetchingMetadata = ref(false) |
63 | | -const isUploading = ref(false) |
64 | | -const uploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle') |
65 | | -const uploadError = ref('') |
66 | | -
|
67 | | -const wizardData = ref<{ |
68 | | - url: string |
69 | | - metadata: AssetMetadata | null |
70 | | - name: string |
71 | | - tags: string[] |
72 | | -}>({ |
73 | | - url: '', |
74 | | - metadata: null, |
75 | | - name: '', |
76 | | - tags: [] |
77 | | -}) |
78 | | -
|
79 | | -const selectedModelType = ref<string | undefined>(undefined) |
80 | | -
|
81 | | -const { modelTypes, fetchModelTypes } = useModelTypes() |
82 | | -
|
83 | | -// Clear error when URL changes |
84 | | -watch( |
85 | | - () => wizardData.value.url, |
86 | | - () => { |
87 | | - uploadError.value = '' |
88 | | - } |
89 | | -) |
90 | | -
|
91 | | -// Validation |
92 | | -const canFetchMetadata = computed(() => { |
93 | | - return wizardData.value.url.trim().length > 0 |
94 | | -}) |
95 | | -
|
96 | | -const canUploadModel = computed(() => { |
97 | | - return !!selectedModelType.value |
98 | | -}) |
| 60 | +const { |
| 61 | + currentStep, |
| 62 | + isFetchingMetadata, |
| 63 | + isUploading, |
| 64 | + uploadStatus, |
| 65 | + uploadError, |
| 66 | + wizardData, |
| 67 | + selectedModelType, |
| 68 | + canFetchMetadata, |
| 69 | + canUploadModel, |
| 70 | + fetchMetadata, |
| 71 | + uploadModel, |
| 72 | + goToPreviousStep |
| 73 | +} = useUploadModelWizard(modelTypes) |
99 | 74 |
|
100 | 75 | async function handleFetchMetadata() { |
101 | | - if (!canFetchMetadata.value) return |
102 | | -
|
103 | | - // Validate that URL is from Civitai domain |
104 | | - const isCivitaiUrl = (url: string): boolean => { |
105 | | - try { |
106 | | - const hostname = new URL(url).hostname.toLowerCase() |
107 | | - return hostname === 'civitai.com' || hostname.endsWith('.civitai.com') |
108 | | - } catch { |
109 | | - return false |
110 | | - } |
111 | | - } |
112 | | -
|
113 | | - if (!isCivitaiUrl(wizardData.value.url)) { |
114 | | - uploadError.value = 'Only Civitai URLs are supported' |
115 | | - return |
116 | | - } |
117 | | -
|
118 | | - isFetchingMetadata.value = true |
119 | | - try { |
120 | | - const metadata = await assetService.getAssetMetadata(wizardData.value.url) |
121 | | - wizardData.value.metadata = metadata |
122 | | -
|
123 | | - // Pre-fill name from metadata |
124 | | - wizardData.value.name = metadata.filename || metadata.name || '' |
125 | | -
|
126 | | - // Pre-fill model type from metadata tags if available |
127 | | - if (metadata.tags && metadata.tags.length > 0) { |
128 | | - wizardData.value.tags = metadata.tags |
129 | | - // Try to detect model type from tags |
130 | | - const typeTag = metadata.tags.find((tag) => |
131 | | - modelTypes.value.some((type) => type.value === tag) |
132 | | - ) |
133 | | - if (typeTag) { |
134 | | - selectedModelType.value = typeTag |
135 | | - } |
136 | | - } |
137 | | -
|
138 | | - currentStep.value = 2 |
139 | | - } catch (error) { |
140 | | - console.error('Failed to retrieve metadata:', error) |
141 | | - uploadError.value = |
142 | | - error instanceof Error |
143 | | - ? error.message |
144 | | - : st( |
145 | | - 'assetBrowser.uploadModelFailedToRetrieveMetadata', |
146 | | - 'Failed to retrieve metadata. Please check the link and try again.' |
147 | | - ) |
148 | | - currentStep.value = 1 |
149 | | - } finally { |
150 | | - isFetchingMetadata.value = false |
151 | | - } |
| 76 | + await fetchMetadata() |
152 | 77 | } |
153 | 78 |
|
154 | 79 | async function handleUploadModel() { |
155 | | - if (!canUploadModel.value) return |
156 | | -
|
157 | | - isUploading.value = true |
158 | | - uploadStatus.value = 'uploading' |
159 | | -
|
160 | | - try { |
161 | | - const tags = selectedModelType.value |
162 | | - ? ['models', selectedModelType.value] |
163 | | - : ['models'] |
164 | | - const filename = |
165 | | - wizardData.value.metadata?.filename || |
166 | | - wizardData.value.metadata?.name || |
167 | | - 'model' |
168 | | -
|
169 | | - await assetService.uploadAssetFromUrl({ |
170 | | - url: wizardData.value.url, |
171 | | - name: filename, |
172 | | - tags, |
173 | | - user_metadata: { |
174 | | - source: 'civitai', |
175 | | - source_url: wizardData.value.url, |
176 | | - model_type: selectedModelType.value |
177 | | - } |
178 | | - }) |
179 | | -
|
180 | | - uploadStatus.value = 'success' |
181 | | - currentStep.value = 3 |
| 80 | + const success = await uploadModel() |
| 81 | + if (success) { |
182 | 82 | emit('upload-success') |
183 | | - } catch (error) { |
184 | | - console.error('Failed to upload asset:', error) |
185 | | - uploadStatus.value = 'error' |
186 | | - uploadError.value = |
187 | | - error instanceof Error ? error.message : 'Failed to upload model' |
188 | | - currentStep.value = 3 |
189 | | - } finally { |
190 | | - isUploading.value = false |
191 | | - } |
192 | | -} |
193 | | -
|
194 | | -function goToPreviousStep() { |
195 | | - if (currentStep.value > 1) { |
196 | | - currentStep.value = currentStep.value - 1 |
197 | 83 | } |
198 | 84 | } |
199 | 85 |
|
|
0 commit comments