|
5 | 5 | using AsyncAwaitBestPractices; |
6 | 6 | using Avalonia.Controls.Notifications; |
7 | 7 | using Injectio.Attributes; |
| 8 | +<<<<<<< HEAD |
| 9 | +======= |
| 10 | +using Python.Runtime; |
| 11 | +using StabilityMatrix.Core.Extensions; |
| 12 | +>>>>>>> 894c1abe (Merge pull request #980 from ionite34/fix-model-overwrite) |
8 | 13 | using StabilityMatrix.Core.Models; |
9 | 14 | using StabilityMatrix.Core.Models.Api; |
10 | 15 | using StabilityMatrix.Core.Models.FileInterfaces; |
11 | 16 | using StabilityMatrix.Core.Models.Progress; |
12 | 17 | using StabilityMatrix.Core.Services; |
| 18 | +using Dispatcher = Avalonia.Threading.Dispatcher; |
13 | 19 |
|
14 | 20 | namespace StabilityMatrix.Avalonia.Services; |
15 | 21 |
|
@@ -117,6 +123,23 @@ public async Task DoImport( |
117 | 123 | modelFile.Name = Path.GetInvalidFileNameChars() |
118 | 124 | .Aggregate(modelFile.Name, (current, c) => current.Replace(c, '_')); |
119 | 125 |
|
| 126 | + // New code: Ensure unique file name |
| 127 | + var originalFileName = modelFile.Name; |
| 128 | + var uniqueFileName = GenerateUniqueFileName(downloadFolder.ToString(), originalFileName); |
| 129 | + if (!uniqueFileName.Equals(originalFileName, StringComparison.Ordinal)) |
| 130 | + { |
| 131 | + Dispatcher.UIThread.Post(() => |
| 132 | + { |
| 133 | + notificationService.Show( |
| 134 | + new Notification( |
| 135 | + "File renamed", |
| 136 | + $"A file with the name \"{originalFileName}\" already exists. The model will be saved as \"{uniqueFileName}\"." |
| 137 | + ) |
| 138 | + ); |
| 139 | + }); |
| 140 | + modelFile.Name = uniqueFileName; |
| 141 | + } |
| 142 | + |
120 | 143 | var downloadPath = downloadFolder.JoinFile(modelFile.Name); |
121 | 144 |
|
122 | 145 | // Download model info and preview first |
@@ -163,4 +186,138 @@ public async Task DoImport( |
163 | 186 |
|
164 | 187 | await trackedDownloadService.TryStartDownload(download); |
165 | 188 | } |
| 189 | +<<<<<<< HEAD |
| 190 | +======= |
| 191 | + |
| 192 | + public Task DoOpenModelDbImport( |
| 193 | + OpenModelDbKeyedModel model, |
| 194 | + OpenModelDbResource resource, |
| 195 | + DirectoryPath downloadFolder, |
| 196 | + Action<TrackedDownload>? configureDownload = null |
| 197 | + ) |
| 198 | + { |
| 199 | + // todo: maybe can get actual filename from url? |
| 200 | + ArgumentException.ThrowIfNullOrEmpty(model.Id, nameof(model)); |
| 201 | + ArgumentException.ThrowIfNullOrEmpty(resource.Type, nameof(resource)); |
| 202 | + var modelFileName = $"{model.Id}.{resource.Type}"; |
| 203 | + |
| 204 | + var modelUris = resource.Urls?.Select(u => new Uri(u, UriKind.Absolute)).ToArray(); |
| 205 | + if (modelUris is null || modelUris.Length == 0) |
| 206 | + { |
| 207 | + notificationService.Show( |
| 208 | + new Notification( |
| 209 | + "Model has no download links", |
| 210 | + "This model has no download links available", |
| 211 | + NotificationType.Warning |
| 212 | + ) |
| 213 | + ); |
| 214 | + return Task.CompletedTask; |
| 215 | + } |
| 216 | + |
| 217 | + return DoCustomImport( |
| 218 | + modelUris, |
| 219 | + modelFileName, |
| 220 | + downloadFolder, |
| 221 | + model.Images?.SelectImageAbsoluteUris().FirstOrDefault(), |
| 222 | + configureDownload: configureDownload, |
| 223 | + connectedModelInfo: new ConnectedModelInfo(model, resource, DateTimeOffset.Now) |
| 224 | + ); |
| 225 | + } |
| 226 | + |
| 227 | + public async Task DoCustomImport( |
| 228 | + IEnumerable<Uri> modelUris, |
| 229 | + string modelFileName, |
| 230 | + DirectoryPath downloadFolder, |
| 231 | + Uri? previewImageUri = null, |
| 232 | + string? previewImageFileExtension = null, |
| 233 | + ConnectedModelInfo? connectedModelInfo = null, |
| 234 | + Action<TrackedDownload>? configureDownload = null |
| 235 | + ) |
| 236 | + { |
| 237 | + // Folders might be missing if user didn't install any packages yet |
| 238 | + downloadFolder.Create(); |
| 239 | + |
| 240 | + // Fix invalid chars in FileName |
| 241 | + var modelBaseFileName = Path.GetFileNameWithoutExtension(modelFileName); |
| 242 | + modelBaseFileName = Path.GetInvalidFileNameChars() |
| 243 | + .Aggregate(modelBaseFileName, (current, c) => current.Replace(c, '_')); |
| 244 | + var modelFileExtension = Path.GetExtension(modelFileName); |
| 245 | + |
| 246 | + var downloadPath = downloadFolder.JoinFile(modelBaseFileName + modelFileExtension); |
| 247 | + |
| 248 | + // Save model info and preview image first if available |
| 249 | + var cleanupFilePaths = new List<string>(); |
| 250 | + if (connectedModelInfo is not null) |
| 251 | + { |
| 252 | + await connectedModelInfo.SaveJsonToDirectory(downloadFolder, modelBaseFileName); |
| 253 | + cleanupFilePaths.Add( |
| 254 | + downloadFolder.JoinFile(modelBaseFileName + ConnectedModelInfo.FileExtension) |
| 255 | + ); |
| 256 | + } |
| 257 | + if (previewImageUri is not null) |
| 258 | + { |
| 259 | + if (previewImageFileExtension is null) |
| 260 | + { |
| 261 | + previewImageFileExtension = Path.GetExtension(previewImageUri.LocalPath); |
| 262 | + if (string.IsNullOrEmpty(previewImageFileExtension)) |
| 263 | + { |
| 264 | + throw new InvalidOperationException( |
| 265 | + "Unable to get preview image file extension from from Uri, and no file extension provided" |
| 266 | + ); |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + var previewImageDownloadPath = downloadFolder.JoinFile( |
| 271 | + modelBaseFileName + ".preview" + previewImageFileExtension |
| 272 | + ); |
| 273 | + |
| 274 | + await notificationService.TryAsync( |
| 275 | + downloadService.DownloadToFileAsync(previewImageUri.ToString(), previewImageDownloadPath), |
| 276 | + "Could not download preview image" |
| 277 | + ); |
| 278 | + |
| 279 | + cleanupFilePaths.Add(previewImageDownloadPath); |
| 280 | + } |
| 281 | + |
| 282 | + // Create tracked download |
| 283 | + // todo: support multiple uris |
| 284 | + var modelUri = modelUris.First(); |
| 285 | + var download = trackedDownloadService.NewDownload(modelUri, downloadPath); |
| 286 | + |
| 287 | + // Add hash info |
| 288 | + // download.ExpectedHashSha256 = modelFile.Hashes.SHA256; |
| 289 | + |
| 290 | + // Add files to cleanup list |
| 291 | + download.ExtraCleanupFileNames.AddRange(cleanupFilePaths); |
| 292 | + |
| 293 | + // Configure |
| 294 | + configureDownload?.Invoke(download); |
| 295 | + |
| 296 | + // Add hash context action |
| 297 | + // download.ContextAction = CivitPostDownloadContextAction.FromCivitFile(modelFile); |
| 298 | + |
| 299 | + await trackedDownloadService.TryStartDownload(download); |
| 300 | + } |
| 301 | + |
| 302 | + private string GenerateUniqueFileName(string folder, string fileName) |
| 303 | + { |
| 304 | + var fullPath = Path.Combine(folder, fileName); |
| 305 | + if (!File.Exists(fullPath)) |
| 306 | + return fileName; |
| 307 | + |
| 308 | + var name = Path.GetFileNameWithoutExtension(fileName); |
| 309 | + var extension = Path.GetExtension(fileName); |
| 310 | + var count = 1; |
| 311 | + string newFileName; |
| 312 | + |
| 313 | + do |
| 314 | + { |
| 315 | + newFileName = $"{name} ({count}){extension}"; |
| 316 | + fullPath = Path.Combine(folder, newFileName); |
| 317 | + count++; |
| 318 | + } while (File.Exists(fullPath)); |
| 319 | + |
| 320 | + return newFileName; |
| 321 | + } |
| 322 | +>>>>>>> 894c1abe (Merge pull request #980 from ionite34/fix-model-overwrite) |
166 | 323 | } |
0 commit comments