Skip to content

Commit 9934870

Browse files
authored
Merge pull request LykosAI#1642 from LykosAI/main
v2.15.8
2 parents 9650e2d + bdb77d5 commit 9934870

13 files changed

Lines changed: 340 additions & 76 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,23 @@ All notable changes to Stability Matrix will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased - v2.15.8]
8+
## v2.15.8
9+
### Added
10+
- Added support for the civitai.red (mature-content) domain — NSFW CivitAI links now open and copy as civitai.red URLs, and pasting a civitai.red URL into the CivitAI model browser search works the same as a civitai.com URL
11+
### Changed
12+
- The CivitAI base model type filter now uses CivitAI's official `/api/v1/enums` endpoint, with fallbacks to the previous technique and a built-in list, so the filter stays populated even if the CivitAI response format changes or the service is unreachable
913
### Fixed
1014
- Fixed [#1608](https://github.com/LykosAI/StabilityMatrix/issues/1608) - Crash when cdn fetch fails due to error notification not being shown on UI Thread - thanks to @NeuralFault!
15+
- Fixed CivitAI model browsing breaking during Discovery API outages — the browser now falls back to the direct CivitAI API when Discovery returns a server error, authentication failure, or times out
16+
- Fixed SwarmUI user settings (theme, output format, server configuration, etc.) and any user-added backend entries being overwritten when the install flow ran over an existing install — `Settings.fds` and `Backends.fds` are now merged with their existing contents instead of being rewritten from a stale template
17+
- Fixed pip requirements handling for environment-marker dependencies - thanks to @NeuralFault!
18+
- Fixed [#1608](https://github.com/LykosAI/StabilityMatrix/issues/1608) - Crash when cdn fetch fails due to error notification not being shown on UI Thread - thanks to @NeuralFault!
19+
- Fixed ComfyUI-Zluda inheriting `--enable-manager` from the base ComfyUI launch options, which blocked the bundled custom-node manager from initializing - thanks to @NeuralFault!
20+
### Supporters
21+
#### 🌟 Visionaries
22+
Heaps of gratitude to our Visionaries — **Waterclouds**, **bluepopsicle**, **Ibixat**, **Droolguy**, **snotty**, **LG**, and **whudunit** — for sticking with us release after release. Your encouragement, your patience while we chase down those last bugs, and the sheer fact of you being here keeps us showing up at the keyboard. We're so glad you're part of this little corner of the internet with us. And big warm welcomes again to our newest Visionaries **MrMxyzptlk12836**, **Psilocyfer18731**, **KalAbaddon**, **RustCupcake**, and **moon_milky2843** — make yourselves at home, you're among friends! 💛
23+
#### 🚀 Pioneers
24+
And the Pioneer crew — what a lineup. A massive thank-you to **Szir777**, **[USA]TechDude**, **takyamtom**, **SinthCore**, **Commissar Lord Death**, **Ahmed S**, **SeraphOfSalem**, and **Jisuren** — your steady presence, kind words, and patience as we've shifted things around mean more than you know. A heartfelt welcome back to **Tigon**, who's returned to the Pioneer ranks after a little time away — so glad you're back. 🎉 And a special hello to **jweg79**, who's been quietly supporting us for a while and just decided to step up and join the Pioneer crew this round — so happy to have you here. To our newest Pioneers, an enormous welcome: **rwx14662**, **Hurbie53**, **ahnhj.al**, **drew.lukas**, **Firelight**, **joeto332987**, **Tuskaruho**, **Cjloha**, **Alligator1907**, **Bitti**, **damianpointdexter**, and **tmdcks**! We're absolutely thrilled to have you with us. (And to our anonymous Pioneer out there too, our thanks reaches you — we see you. 💛)
1125

1226
## v2.15.7
1327
### Added

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<PackageVersion Include="DynamicData" Version="9.3.1" />
2525
<PackageVersion Include="ExifLibNet" Version="2.1.4" />
2626
<PackageVersion Include="Exceptionless.DateTimeExtensions" Version="3.4.3" />
27-
<PackageVersion Include="FreneticLLC.FreneticUtilities" Version="1.0.32" />
27+
<PackageVersion Include="FreneticLLC.FreneticUtilities" Version="1.1.4" />
2828
<PackageVersion Include="FuzzySharp" Version="2.0.2" />
2929
<PackageVersion Include="Hardware.Info" Version="100.1.0.1" />
3030
<PackageVersion Include="Injectio" Version="4.0.0" />
@@ -133,4 +133,4 @@
133133
<PackageVersion Include="xunit" Version="2.9.0" />
134134
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
135135
</ItemGroup>
136-
</Project>
136+
</Project>

StabilityMatrix.Avalonia/Services/CivitBaseModelTypeService.cs

Lines changed: 146 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,65 @@ ILiteDbContext dbContext
1818
{
1919
private const string CacheId = "BaseModelTypes";
2020
private static readonly TimeSpan CacheExpiration = TimeSpan.FromHours(24);
21+
private const string LegacyBaseModelListProbe = "gimmethelist";
22+
private static readonly IReadOnlyList<string> KnownVisibleBaseModelTypes =
23+
[
24+
"Anima",
25+
"AuraFlow",
26+
"Chroma",
27+
"CogVideoX",
28+
"Flux.1 D",
29+
"Flux.1 Kontext",
30+
"Flux.1 Krea",
31+
"Flux.1 S",
32+
"Flux.2 D",
33+
"Flux.2 Klein 4B",
34+
"Flux.2 Klein 4B-base",
35+
"Flux.2 Klein 9B",
36+
"Flux.2 Klein 9B-base",
37+
"Grok",
38+
"HiDream",
39+
"Hunyuan 1",
40+
"Hunyuan Video",
41+
"Illustrious",
42+
"Kolors",
43+
"LTXV",
44+
"LTXV 2.3",
45+
"LTXV2",
46+
"Lumina",
47+
"Mochi",
48+
"NoobAI",
49+
"Other",
50+
"PixArt E",
51+
"PixArt a",
52+
"Pony",
53+
"Pony V7",
54+
"Qwen",
55+
"Qwen 2",
56+
"SD 1.4",
57+
"SD 1.5",
58+
"SD 1.5 Hyper",
59+
"SD 1.5 LCM",
60+
"SD 2.0",
61+
"SD 2.1",
62+
"SDXL 1.0",
63+
"SDXL Hyper",
64+
"SDXL Lightning",
65+
"Upscaler",
66+
"Wan Image 2.7",
67+
"Wan Video 1.3B t2v",
68+
"Wan Video 14B i2v 480p",
69+
"Wan Video 14B i2v 720p",
70+
"Wan Video 14B t2v",
71+
"Wan Video 2.2 I2V-A14B",
72+
"Wan Video 2.2 T2V-A14B",
73+
"Wan Video 2.2 TI2V-5B",
74+
"Wan Video 2.5 I2V",
75+
"Wan Video 2.5 T2V",
76+
"Wan Video 2.7",
77+
"ZImageBase",
78+
"ZImageTurbo",
79+
];
2180

2281
/// <summary>
2382
/// Gets the list of base model types, using cache if available and not expired
@@ -39,15 +98,11 @@ public async Task<List<string>> GetBaseModelTypes(bool forceRefresh = false, boo
3998
{
4099
if (civitBaseModels.Count <= 0)
41100
{
42-
var baseModelsResponse = await civitApi.GetBaseModelList();
43-
var jsonContent = await baseModelsResponse.Content.ReadAsStringAsync();
44-
var baseModels = JsonNode.Parse(jsonContent);
101+
civitBaseModels =
102+
await TryGetBaseModelsFromEnumsEndpoint() ?? await TryGetLegacyBaseModelList() ?? [];
45103

46-
var innerJson = baseModels?["error"]?["message"]?.GetValue<string>();
47-
var jArray = JsonNode.Parse(innerJson).AsArray();
48-
var baseModelValues = jArray[0]?["errors"]?[0]?[0]?["values"]?.AsArray();
49-
50-
civitBaseModels = baseModelValues?.GetValues<string>().ToList() ?? [];
104+
civitBaseModels =
105+
civitBaseModels.Count > 0 ? civitBaseModels : GetKnownVisibleBaseModelTypes();
51106

52107
// Cache the results
53108
var cacheEntry = new CivitBaseModelTypeCacheEntry
@@ -60,27 +115,18 @@ public async Task<List<string>> GetBaseModelTypes(bool forceRefresh = false, boo
60115
await dbContext.UpsertCivitBaseModelTypeCacheEntry(cacheEntry);
61116
}
62117

63-
if (includeAllOption)
64-
{
65-
civitBaseModels.Insert(0, CivitBaseModelType.All.ToString());
66-
}
67-
68-
// Filter and sort results
69-
var filteredResults = civitBaseModels
70-
.Where(s => !s.Equals("odor", StringComparison.OrdinalIgnoreCase))
71-
.OrderBy(s => s)
72-
.ToList();
73-
74-
return filteredResults;
118+
return NormalizeBaseModelTypes(civitBaseModels, includeAllOption);
75119
}
76120
catch (Exception e)
77121
{
78122
logger.LogError(e, "Failed to get base model list");
79123

80124
// Return cached results if available, even if expired
81125
var expiredCache = await dbContext.GetCivitBaseModelTypeCacheEntry(CacheId);
82-
return expiredCache?.ModelTypes
83-
?? Enum.GetValues<CivitBaseModelType>().Select(b => b.GetStringValue()).ToList();
126+
return NormalizeBaseModelTypes(
127+
expiredCache?.ModelTypes ?? GetKnownVisibleBaseModelTypes(),
128+
includeAllOption
129+
);
84130
}
85131
}
86132

@@ -91,4 +137,82 @@ public void ClearCache()
91137
{
92138
dbContext.CivitBaseModelTypeCache.DeleteAllAsync();
93139
}
140+
141+
private async Task<List<string>?> TryGetBaseModelsFromEnumsEndpoint()
142+
{
143+
try
144+
{
145+
var enumsResponse = await civitApi.GetEnums();
146+
var baseModels = enumsResponse?.ActiveBaseModel ?? enumsResponse?.BaseModel;
147+
148+
if (baseModels is { Count: > 0 })
149+
{
150+
return baseModels;
151+
}
152+
153+
logger.LogInformation(
154+
"CivitAI enums endpoint returned no base models; falling back to legacy/base list"
155+
);
156+
return null;
157+
}
158+
catch (Exception ex)
159+
{
160+
logger.LogInformation(ex, "CivitAI enums endpoint failed; falling back to legacy/base list");
161+
return null;
162+
}
163+
}
164+
165+
private async Task<List<string>?> TryGetLegacyBaseModelList()
166+
{
167+
var baseModelsResponse = await civitApi.GetBaseModelList();
168+
var jsonContent = await baseModelsResponse.Content.ReadAsStringAsync();
169+
return TryParseLegacyBaseModelList(jsonContent);
170+
}
171+
172+
private List<string>? TryParseLegacyBaseModelList(string jsonContent)
173+
{
174+
var baseModels = JsonNode.Parse(jsonContent);
175+
var innerJson = baseModels?["error"]?["message"]?.GetValue<string>();
176+
if (string.IsNullOrWhiteSpace(innerJson))
177+
{
178+
logger.LogInformation(
179+
"CivitAI base model probe value '{Probe}' no longer returns the legacy validation payload; using built-in base model list",
180+
LegacyBaseModelListProbe
181+
);
182+
return null;
183+
}
184+
185+
var jArray = JsonNode.Parse(innerJson)?.AsArray();
186+
var baseModelValues = jArray?[0]?["errors"]?[0]?[0]?["values"]?.AsArray();
187+
return baseModelValues?.GetValues<string>().ToList();
188+
}
189+
190+
private static List<string> GetKnownVisibleBaseModelTypes()
191+
{
192+
return KnownVisibleBaseModelTypes.ToList();
193+
}
194+
195+
private static List<string> NormalizeBaseModelTypes(
196+
IEnumerable<string>? baseModels,
197+
bool includeAllOption
198+
)
199+
{
200+
var normalized = (baseModels ?? [])
201+
.Where(s => !string.IsNullOrWhiteSpace(s))
202+
.Where(s => !s.Equals("odor", StringComparison.OrdinalIgnoreCase))
203+
.Distinct(StringComparer.OrdinalIgnoreCase)
204+
.OrderBy(s => s)
205+
.ToList();
206+
207+
normalized.RemoveAll(s =>
208+
s.Equals(CivitBaseModelType.All.ToString(), StringComparison.OrdinalIgnoreCase)
209+
);
210+
211+
if (includeAllOption)
212+
{
213+
normalized.Insert(0, CivitBaseModelType.All.ToString());
214+
}
215+
216+
return normalized;
217+
}
94218
}

StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ private void UpdateImage()
191191
[RelayCommand]
192192
private void OpenModel()
193193
{
194-
ProcessRunner.OpenUrl($"https://civitai.com/models/{CivitModel.Id}");
194+
ProcessRunner.OpenUrl(CivitaiUrlHelper.GetModelUrl(CivitModel.Id, CivitModel.Nsfw));
195195
}
196196

197197
[RelayCommand]

StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -719,22 +719,18 @@ private async Task SearchModels(bool isInfiniteScroll = false)
719719
modelRequest.Sort = CivitSortMode.HighestRated;
720720
}
721721
}
722-
else if (SearchQuery.StartsWith("https://civitai.com/models/"))
722+
else if (CivitaiUrlHelper.TryParseModelId(SearchQuery, out var modelId))
723723
{
724724
/* extract model ID from URL, could be one of:
725725
https://civitai.com/models/443821?modelVersionId=1957537
726+
https://civitai.red/models/443821?modelVersionId=1957537
726727
https://civitai.com/models/443821/cyberrealistic-pony
727728
https://civitai.com/models/443821
728729
*/
729-
var modelId = SearchQuery
730-
.Replace("https://civitai.com/models/", string.Empty)
731-
.Split(['?', '/'], StringSplitOptions.RemoveEmptyEntries)
732-
.FirstOrDefault();
733-
734730
modelRequest.Period = CivitPeriod.AllTime;
735731
modelRequest.BaseModels = null;
736732
modelRequest.Types = null;
737-
modelRequest.CommaSeparatedModelIds = modelId;
733+
modelRequest.CommaSeparatedModelIds = modelId.ToString();
738734

739735
if (modelRequest.Sort is CivitSortMode.Favorites or CivitSortMode.Installed)
740736
{

StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitDetailsPageViewModel.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ IModelImportService modelImportService
5555
) : DisposableViewModelBase
5656
{
5757
[ObservableProperty]
58-
[NotifyPropertyChangedFor(nameof(ShowInferenceDefaultsSection))]
58+
[NotifyPropertyChangedFor(nameof(ShowInferenceDefaultsSection), nameof(CivitUrl))]
5959
public required partial CivitModel CivitModel { get; set; }
6060

6161
[ObservableProperty]
@@ -110,7 +110,8 @@ IModelImportService modelImportService
110110
nameof(ShortSha256),
111111
nameof(BaseModelType),
112112
nameof(ModelFileNameFormat),
113-
nameof(IsEarlyAccess)
113+
nameof(IsEarlyAccess),
114+
nameof(CivitUrl)
114115
)]
115116
public partial ModelVersionViewModel? SelectedVersion { get; set; }
116117

@@ -172,7 +173,8 @@ IModelImportService modelImportService
172173

173174
public bool IsEarlyAccess => SelectedVersion?.ModelVersion.IsEarlyAccess ?? false;
174175

175-
public string CivitUrl => $@"https://civitai.com/models/{CivitModel.Id}";
176+
public string CivitUrl =>
177+
CivitaiUrlHelper.GetModelUrl(CivitModel.Id, CivitModel.Nsfw, SelectedVersion?.ModelVersion.Id);
176178

177179
public int DescriptionRowSpan => string.IsNullOrWhiteSpace(ModelVersionDescription) ? 3 : 1;
178180

StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFileViewModel.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,13 @@ private void OpenOnCivitAi()
133133
{
134134
if (CheckpointFile.ConnectedModelInfo?.ModelId == null)
135135
return;
136-
ProcessRunner.OpenUrl($"https://civitai.com/models/{CheckpointFile.ConnectedModelInfo.ModelId}");
136+
ProcessRunner.OpenUrl(
137+
CivitaiUrlHelper.GetModelUrl(
138+
CheckpointFile.ConnectedModelInfo.ModelId.Value,
139+
CheckpointFile.ConnectedModelInfo.Nsfw,
140+
CheckpointFile.ConnectedModelInfo.VersionId
141+
)
142+
);
137143
}
138144

139145
[RelayCommand]
@@ -149,7 +155,11 @@ private Task CopyModelUrl()
149155
Task.CompletedTask,
150156
ConnectedModelSource.Civitai when CheckpointFile.ConnectedModelInfo.ModelId != null =>
151157
App.Clipboard.SetTextAsync(
152-
$"https://civitai.com/models/{CheckpointFile.ConnectedModelInfo.ModelId}"
158+
CivitaiUrlHelper.GetModelUrl(
159+
CheckpointFile.ConnectedModelInfo.ModelId.Value,
160+
CheckpointFile.ConnectedModelInfo.Nsfw,
161+
CheckpointFile.ConnectedModelInfo.VersionId
162+
)
153163
),
154164

155165
ConnectedModelSource.OpenModelDb => App.Clipboard.SetTextAsync(

0 commit comments

Comments
 (0)