Compare commits
2220 Commits
Author | SHA1 | Date | |
---|---|---|---|
e706fae648 | |||
118a4862ab | |||
5e2f31e3bf | |||
f78b31b1bc | |||
8d698cb997 | |||
8945aac319 | |||
f2a960136e | |||
7a1170f1dd | |||
24cce08580 | |||
b425b43d3e | |||
353fe88226 | |||
1a3086230e | |||
0e57487774 | |||
3024465086 | |||
c95b43253a | |||
aedf7856e5 | |||
d83e034d5e | |||
b9676b51cb | |||
5698473891 | |||
de1d1ad961 | |||
bd82480fa3 | |||
fce8b96d3b | |||
37b47e7f05 | |||
a6f94959fe | |||
45a2c9f7ef | |||
c49ac6880d | |||
e0258d9e7b | |||
e3ff6f183b | |||
e6ec7393c6 | |||
f733b53c25 | |||
204a68b17d | |||
1379dde1a7 | |||
79eee62d42 | |||
7c1f18b6cd | |||
b59371988d | |||
30dbadb2ab | |||
a342de0207 | |||
6e6d236819 | |||
0e41483564 | |||
1023f5f7cc | |||
4bc7bca60d | |||
de7dbd27c0 | |||
14118f142c | |||
9b99be4c1d | |||
91c4b5865c | |||
1b4c14af71 | |||
7b85e50604 | |||
d64b2d8fbe | |||
f1a7aed1b6 | |||
75f758e792 | |||
e25e1bfe10 | |||
09deaefab0 | |||
f80ecbde71 | |||
5e1e198a1f | |||
bdbb741716 | |||
2f0e8a8a4a | |||
4f8424c544 | |||
ce3355d6aa | |||
fb67ef2df0 | |||
380e9aaf13 | |||
255e90d125 | |||
504f7f3799 | |||
9970e505de | |||
0ccacd5eca | |||
50e4683492 | |||
bc14bdc010 | |||
14b0dabfdf | |||
e140acd2a4 | |||
facfed07fe | |||
41a3309cbe | |||
4df9a22dd6 | |||
31a1c4b2b2 | |||
c2c33b7df1 | |||
6a2c2152e2 | |||
37f2755611 | |||
aa70f2849b | |||
e7a2dfa57f | |||
b43f9fc4ee | |||
51b6a2fd2a | |||
5fffb82b16 | |||
e051dbc2c7 | |||
c2fba39cc7 | |||
1050b13bbb | |||
92d3d9cd33 | |||
d8dec3e56a | |||
130f9678b2 | |||
29d13cb06d | |||
620f521e0c | |||
a36fb55b05 | |||
23f9bcb38b | |||
e73e820237 | |||
7e4735ae0f | |||
66ffcbbee6 | |||
4754743c84 | |||
09c1dfd92b | |||
7fc46f3672 | |||
df93fee034 | |||
fc2cf742c8 | |||
9bec441e94 | |||
1caab1da85 | |||
d612d7ab53 | |||
3d3994bbad | |||
d643ae0299 | |||
0a099434a3 | |||
16905a8999 | |||
282c4cca82 | |||
f36b7ce016 | |||
9fb5cac5d4 | |||
9f5f213cd3 | |||
5d3b59b94e | |||
744c6e4725 | |||
c59745d346 | |||
9d1dd09a07 | |||
2eb317c6b6 | |||
0ad08c609d | |||
85f6f8b31d | |||
9799309db9 | |||
fa205f483a | |||
2df4286256 | |||
b89f689ea3 | |||
f58b21746e | |||
6971f9dcf1 | |||
3454a47f67 | |||
5922fd39c5 | |||
cdbddbae3b | |||
af4a26c1d0 | |||
d3f42e47a7 | |||
8821e471b5 | |||
d34aed0b14 | |||
b7391652ca | |||
074a14f056 | |||
b1db708af1 | |||
b2a66709b0 | |||
e3e43913ab | |||
c7fed0a42a | |||
c6c5e0734a | |||
73cbc58a50 | |||
8431395326 | |||
dd21c07d4a | |||
ce9591428e | |||
a801a5d8b6 | |||
04e8458ce2 | |||
4b4fa84879 | |||
1b3df8c4de | |||
7ce223771d | |||
ccf71ed445 | |||
aa7c031e8a | |||
8465bc1bc9 | |||
f2f3ed71d4 | |||
ab7ba35639 | |||
1cc09cbe5f | |||
fe7e398eb4 | |||
6ab3133b33 | |||
ef77c37a7e | |||
1dd165a9c9 | |||
3c74540615 | |||
ad249c4651 | |||
071a4d6f37 | |||
5f2fb19d71 | |||
ce61657f7a | |||
dc54e5bdce | |||
f7b8e000c5 | |||
73abf131a6 | |||
5741af2aba | |||
159af669f6 | |||
a517255653 | |||
573154633b | |||
baa8afd9eb | |||
9e718da70e | |||
4df442f169 | |||
1dc93c7a39 | |||
3d124986d3 | |||
a589d98cd4 | |||
ed9f18e22c | |||
14fb115fc8 | |||
c35a731a60 | |||
4f3d2bd120 | |||
69c8fc3236 | |||
840ff5c363 | |||
8386cd5cf7 | |||
666c2f8771 | |||
b342fa9661 | |||
63bf84fdd5 | |||
070e51fcab | |||
50fd64150e | |||
63c5de2612 | |||
c576d582e2 | |||
026a4b6c76 | |||
7bc95b68c8 | |||
0332cc8cb3 | |||
ce192f4ad7 | |||
cbdb715918 | |||
5537102fd3 | |||
1ea294f15c | |||
e7bf2ee58b | |||
a931aa59a3 | |||
4c8da67bb1 | |||
a0178e15b3 | |||
43a1c3901f | |||
a4c6f28a70 | |||
f8bca93170 | |||
f07d05a490 | |||
b3a988bc0b | |||
e0f22d29e8 | |||
07ee97b862 | |||
19b05659b5 | |||
7e5c7ca1b7 | |||
1156c159f9 | |||
5c6c2303ba | |||
a0a58bcfa8 | |||
8a28b265a3 | |||
86dc08130b | |||
5cd8a732c7 | |||
fafbbf68a4 | |||
0cbb553564 | |||
f4512bb291 | |||
99205b4d03 | |||
d48e6554d5 | |||
d0c4e95de3 | |||
0b3a35c4b6 | |||
ded6a41f86 | |||
f4063e63d3 | |||
23ba912db0 | |||
b99d9db8f9 | |||
b7047dafb2 | |||
368967fbcf | |||
a9d0fc9978 | |||
b6f3d2ec02 | |||
78e917a6fb | |||
96b45385e8 | |||
db47888a75 | |||
51443741b8 | |||
3e7f14af2c | |||
733439da07 | |||
6bff97d6fa | |||
efba81cb66 | |||
b2cc5dcf4b | |||
fab86ddf35 | |||
f3a90ce02d | |||
4886616c48 | |||
dcd8121009 | |||
59adaf6225 | |||
0055cd9b2e | |||
fe89d487f6 | |||
01368ac496 | |||
495064985e | |||
200f8fd245 | |||
64bf4356b4 | |||
8d4d409cd6 | |||
dd4937178f | |||
e12387a377 | |||
5d3fb9091a | |||
b044bc1791 | |||
409ec61be2 | |||
e2ae2715a3 | |||
52458ae273 | |||
79d112ca7b | |||
9b1a9cc7c8 | |||
42f9abdfe3 | |||
66d311258a | |||
0a1197055c | |||
649cbf07e3 | |||
5089ac5ad1 | |||
d99e3f7974 | |||
3d5133209b | |||
b5d1912c94 | |||
a8fba8f3fb | |||
9d9fc1683a | |||
8ee4364065 | |||
152aa7de09 | |||
85c90cbee1 | |||
7302927e4c | |||
df3d00ef94 | |||
bb47835256 | |||
037512ca5c | |||
a13713adaf | |||
ad073252e7 | |||
d24a7a5c5e | |||
192fd223b4 | |||
a671dd8e00 | |||
8b764a8fd3 | |||
aa576e68e3 | |||
ad5508a14d | |||
4fafc8aa67 | |||
0aab3d0f12 | |||
a5d88bdfcc | |||
5173957368 | |||
4b3e3d900d | |||
9ea51b174a | |||
80e265e547 | |||
c3e6e63023 | |||
9b5a262d63 | |||
1309f1480c | |||
12ba5b8096 | |||
156c5f4792 | |||
1da4b3d94a | |||
18aca98e41 | |||
a88afb0956 | |||
bfa1f57930 | |||
a5350eb3cc | |||
3ed4d792b3 | |||
fb0c9405cf | |||
a17a9044ad | |||
73af7f5481 | |||
57ead7f0c0 | |||
bf490c910a | |||
40f806efa8 | |||
226ba8b06e | |||
b11aa4833d | |||
8d9cd0e30b | |||
9532928998 | |||
420f7549a2 | |||
ed64b9bfed | |||
5d5ebfdef6 | |||
567c02bf5d | |||
60f7c73c8a | |||
ac4c5003f1 | |||
d5e76e662f | |||
23d5f85d17 | |||
f75adc1e22 | |||
15a1436c8b | |||
813edec808 | |||
21e3299b7a | |||
f7193966fb | |||
2d9853f1f4 | |||
ced79a187d | |||
7832524963 | |||
58c7f3ba15 | |||
90ec8f0575 | |||
64ced3b3f6 | |||
493526c478 | |||
b86617e3af | |||
f3db6d84fb | |||
f9b9ecf754 | |||
af43a92a2f | |||
4dbdc642f9 | |||
8f2c87ce94 | |||
5149040496 | |||
5b1078e0db | |||
ae31813239 | |||
f6b3cde286 | |||
0f05f9c32c | |||
89170af721 | |||
5fddae589b | |||
19c16af5fa | |||
019f8f69f4 | |||
ad8d1f77df | |||
e82a8a7f3d | |||
ad07aeb041 | |||
451ab7e84c | |||
083390da83 | |||
dc6d48580b | |||
27d69e2ac3 | |||
91274a4df8 | |||
6eafcdfafd | |||
5e44744ff7 | |||
37b293fe74 | |||
280f0be690 | |||
183bc8321c | |||
a973e4d1ef | |||
eed1066967 | |||
2859c94fea | |||
dbcce2ee5d | |||
25071c238c | |||
9995ffb5f3 | |||
c867c35e45 | |||
6f60e88ca6 | |||
11730dcbe4 | |||
e155bac445 | |||
15a4682665 | |||
08675b39f7 | |||
2c7d5adb80 | |||
51c7faee3c | |||
852e129f9c | |||
6eb2d800fa | |||
0a2c70595d | |||
f13e16af15 | |||
f364958c13 | |||
e65150647d | |||
3c435b9593 | |||
871b96a450 | |||
48a3254ad2 | |||
2c0bdd6377 | |||
8cedeb349d | |||
e241ef25e5 | |||
5e553dd958 | |||
19ee87d2cd | |||
72b3598687 | |||
33b120f6cd | |||
0bfb9d00c8 | |||
b1a2d36c2d | |||
517ddca22d | |||
41c7b08418 | |||
c7c1b5a570 | |||
87b6dfb1a9 | |||
46c56f3706 | |||
32bab80508 | |||
b6f1194c93 | |||
206f9b97bb | |||
13721f160e | |||
102e5623f7 | |||
9a975321db | |||
6743ec14f1 | |||
daec5e5426 | |||
a2b55c0df7 | |||
01320ac735 | |||
84bddee2ce | |||
e636dd3649 | |||
5f6b798e35 | |||
9137f3793e | |||
73e92a688f | |||
7a9f219037 | |||
a4728190c0 | |||
04d67a24b6 | |||
55049ba9d2 | |||
e0b33a4feb | |||
fb5c0a3db7 | |||
8154a5709b | |||
3a6780bd50 | |||
b7a76d4212 | |||
ba7cae683a | |||
243556656e | |||
6662dc66d5 | |||
107112d1c4 | |||
4eae540086 | |||
21108650f7 | |||
c5d343750c | |||
09b76dcd93 | |||
b87bc033f5 | |||
fb95d76e34 | |||
4e765a7948 | |||
cf2408013e | |||
d8543d1358 | |||
d8b79d8b5c | |||
c2bcf89f9a | |||
5cb24f992c | |||
21394b7d45 | |||
6d08082693 | |||
768fb2583a | |||
6e07b2354f | |||
00597879bc | |||
0cd0d6aadf | |||
9d201f82f1 | |||
d6c535c45c | |||
babdb5b718 | |||
0ea8d038be | |||
c804a9971e | |||
4d7f6e4236 | |||
5474d1786f | |||
7f36473544 | |||
9d19698bf3 | |||
582b2d936f | |||
6036ccdc1c | |||
5eeef41d8c | |||
bacf266f0d | |||
ba5c54043b | |||
e33c858829 | |||
e47e54de3f | |||
54f9e9bfe9 | |||
e1875c872c | |||
27b8e173e8 | |||
47e3884994 | |||
e483071894 | |||
af090cb289 | |||
9bbb25f16c | |||
3007f00c9b | |||
352dcfbe30 | |||
60b181a545 | |||
600482e2d7 | |||
39ccbbd72e | |||
6e69cbcdaf | |||
bf6c222a3b | |||
6afcf7570a | |||
c3126f7b4d | |||
cb3b542363 | |||
1a5e15608c | |||
64a751ad79 | |||
57efe31959 | |||
39350d554b | |||
8f4e03550c | |||
d03823fb20 | |||
00ec2b9d6f | |||
70e4bc4582 | |||
5e56a437ef | |||
22ffd25619 | |||
127949c56b | |||
cdfef16a0e | |||
1595f1ed05 | |||
1cae39b105 | |||
8189b38e6e | |||
c240d6932a | |||
c4548d9396 | |||
aea70e3dd4 | |||
3b01e65e11 | |||
341c810bbb | |||
85fd2dfaaa | |||
bf4bc38c6c | |||
aa8b50280b | |||
62553dc0fa | |||
25639cc3f8 | |||
7982a9ae25 | |||
aa01fd058e | |||
ef7e1575bd | |||
fb075a0013 | |||
d1738baf44 | |||
7eb29fa91b | |||
34c00fb77f | |||
7965318d9f | |||
e73a514e29 | |||
35ff4f439e | |||
12e0194c7f | |||
d1ac90e16d | |||
7dc7f70582 | |||
84d606408a | |||
d103693811 | |||
0dbce101ac | |||
cb81e2aacd | |||
6cd0b530c5 | |||
35571eb14d | |||
8e6102ad9a | |||
80bc80dc2c | |||
a483bd0800 | |||
47a39569bc | |||
f00e1a92d8 | |||
a289945e8e | |||
b750c0d7c3 | |||
a244a6873a | |||
ceff4f06c1 | |||
0307114c8e | |||
92030a3917 | |||
73ace121a4 | |||
44d5809e46 | |||
5c4e6f7e96 | |||
8c032579b8 | |||
b53935bfd4 | |||
d4db027cfa | |||
27963decc9 | |||
25f488c6e1 | |||
07bd580050 | |||
fb32a38d96 | |||
ac0961d7d4 | |||
6b943f88d1 | |||
4bbf683d15 | |||
d0e50584ea | |||
b57649828d | |||
1f44a283b3 | |||
9947c3bcfb | |||
8faf6b9f52 | |||
e45cbbf1ca | |||
1a5b6ef260 | |||
096556d8c9 | |||
97919c7e87 | |||
0aa7968503 | |||
bd1bc78953 | |||
6ce6dc3ff6 | |||
e6346775e7 | |||
d03eed3859 | |||
afb88616d8 | |||
543f13f9a3 | |||
af5c68051a | |||
5b7cd11de8 | |||
d3c3496e55 | |||
c08c8b2789 | |||
069315e434 | |||
7e4ad83a1c | |||
400f9fd680 | |||
38951f5581 | |||
b5329ee93d | |||
c568bca69e | |||
7b2be12587 | |||
099fde2652 | |||
83e5410945 | |||
b330c34b29 | |||
e3184622e8 | |||
28f822afe0 | |||
a2af811ad2 | |||
cde8c2d3bd | |||
79cc84b611 | |||
f1de0be679 | |||
854e3d3576 | |||
dbac2655f5 | |||
0f656dbf2f | |||
3fbb3f6773 | |||
8820814002 | |||
b40fb3a422 | |||
aa59575df3 | |||
accfec9007 | |||
16410d90b8 | |||
27c6113287 | |||
f4a6910ab4 | |||
bad89160cc | |||
5782966d63 | |||
ba2c966329 | |||
f8dee7e25f | |||
a8151176d7 | |||
9ee0b7fe2e | |||
fb6a7e04f5 | |||
bfdf487d52 | |||
b7aac1501d | |||
273525e6f9 | |||
064a4938c1 | |||
182236e742 | |||
75cb052cca | |||
d4a378827f | |||
592d5e8c40 | |||
733150111d | |||
cbe91251ac | |||
1283c6483d | |||
f24d3d69af | |||
7984327d81 | |||
ef90832aea | |||
9571b8addc | |||
9601f304a5 | |||
ff43dac2a7 | |||
0a43305455 | |||
54d8224de2 | |||
c9e34457cd | |||
47c8eb304f | |||
2dd39fa218 | |||
cb618efb98 | |||
e7ca8090fd | |||
7861c57317 | |||
f701b8dc29 | |||
bd10a850fa | |||
0f96688a54 | |||
8eeca90d55 | |||
367e7f7065 | |||
ee19eaae62 | |||
8eb3a3536b | |||
cfd50231e1 | |||
1c8ab9e1b4 | |||
6094cd8578 | |||
353c49a40b | |||
277140f218 | |||
ca9413ccf4 | |||
c9a0d090cb | |||
1cd783d3a3 | |||
1ead764a02 | |||
45f7b35954 | |||
6a41540749 | |||
5b47da67f6 | |||
292f68ff97 | |||
3b554d881a | |||
40ebf468d3 | |||
4bc6e51862 | |||
427861cf13 | |||
da3e7a2eb8 | |||
2979f04c82 | |||
1949d8a50c | |||
ee66c799e0 | |||
7c50b8bf94 | |||
141ff74ece | |||
321e5f1ed6 | |||
6d131d9d8e | |||
7e69b8eb31 | |||
4e0b33e6a4 | |||
54f7e6fcb8 | |||
529169c4da | |||
a2c8c99215 | |||
e8bf3fd009 | |||
465676e9ea | |||
af53b57047 | |||
54b5f75905 | |||
4348333497 | |||
cc31110bcf | |||
f7c04bf7a6 | |||
029509ebad | |||
65102bb64d | |||
b96b55c5ce | |||
1f5aba010e | |||
f0b3bea4e3 | |||
84fae2d9e0 | |||
0b96fa112d | |||
c64bcd23d3 | |||
efd9a22bb5 | |||
159c3edfe3 | |||
f74fa8657b | |||
648b142a4b | |||
426f92595e | |||
82a8d9b644 | |||
ff9430b8a2 | |||
2e69ffcb5e | |||
0ea38db7ef | |||
a69d4c279e | |||
2706149399 | |||
3d0cdc1cb6 | |||
ac605e9352 | |||
5432297691 | |||
e37be0f954 | |||
a99209b674 | |||
cb02b5ba18 | |||
69f14edd80 | |||
14714b950d | |||
13654cb8c0 | |||
00276228cf | |||
8583bb8d7b | |||
d48951fe00 | |||
99bdcfa0a5 | |||
e64e1a92e6 | |||
e278e639a3 | |||
c4bad5c454 | |||
da41a74efc | |||
0dc970562a | |||
2d8401473d | |||
9c91f57b19 | |||
f14afcd129 | |||
5c1a3d82d7 | |||
e02a917569 | |||
347fa0fda1 | |||
6510d4cb02 | |||
91e4ccf6f8 | |||
36249874bc | |||
d2b5d6cce9 | |||
b2922741c9 | |||
300f3e27db | |||
d7330b80a9 | |||
acdd7667b7 | |||
8114fa3f5d | |||
4bc5508f38 | |||
e503c6092e | |||
6a8985d8dd | |||
bee67fd883 | |||
a1d75d40aa | |||
29484867ca | |||
7fa983b971 | |||
617a8b2814 | |||
b924d323d4 | |||
a2efda41d3 | |||
642c114501 | |||
02dd3e457d | |||
ea7b28c9d5 | |||
472ab4a9ce | |||
fca84e3edf | |||
b70235ff92 | |||
6eff591df7 | |||
d0b2bf736e | |||
e5c11ea214 | |||
6b6443406d | |||
3452d7852a | |||
f1fa10badd | |||
1267621424 | |||
8a0ec95fe1 | |||
ba30a63407 | |||
c56a2adbcb | |||
2de96d4dc9 | |||
a486f20892 | |||
49535deb2e | |||
7cbf62cf12 | |||
3b0ace3410 | |||
5a9c8e1d87 | |||
daaa65dc0a | |||
ab4e371524 | |||
927fd304b0 | |||
5af84b8e90 | |||
d425dac499 | |||
d056459e76 | |||
3169485f33 | |||
d9b9f80a93 | |||
d429505b71 | |||
72ee708917 | |||
93bbfac29a | |||
040d7a6563 | |||
e8dd930a50 | |||
31c049ebfe | |||
d343a37fb2 | |||
7097175c6f | |||
8e57c49043 | |||
9f036ceefd | |||
ff3ca8b36b | |||
87a7b70a27 | |||
9c71c966ca | |||
6dc99e676e | |||
80ecb82cc2 | |||
7fc9509d4d | |||
3bf5e11f94 | |||
eef9af2266 | |||
fabdf5fe30 | |||
1cc27e524b | |||
8316a002da | |||
888c637f71 | |||
48b7d2587e | |||
923c889de8 | |||
8ae575d67a | |||
b51407486a | |||
a689b34ed1 | |||
aa98e60243 | |||
c3bf767024 | |||
b641f1a230 | |||
9499685dda | |||
80d23cbbbf | |||
efa684c5e8 | |||
2edf64985d | |||
5fe7807462 | |||
e96b9005ca | |||
8c29e735e7 | |||
497e073a8c | |||
d4ce54a3c2 | |||
de37a81902 | |||
d6b8cb718a | |||
ed435d2b72 | |||
2b1f8533b0 | |||
0a21a69a9f | |||
5ebc6b698c | |||
cbc48e31e1 | |||
577dd9048f | |||
ae409dd0ec | |||
cde855e1dc | |||
adcd4368e7 | |||
8bcdb205ed | |||
2cf8b2a453 | |||
6c156380f9 | |||
369d0ee502 | |||
4971a212e9 | |||
2111a81d18 | |||
6799b3d7da | |||
a3463274ee | |||
c10e773401 | |||
f7af259576 | |||
87c6a54634 | |||
d03521bf12 | |||
3eb1919c81 | |||
e53d6dbd5c | |||
01d2db8e96 | |||
b18c2aea05 | |||
a6e3c272e2 | |||
4000f98ba4 | |||
d06fd404ae | |||
c6f0e19e2f | |||
462af9989a | |||
eedea2fdcd | |||
9c3d946de0 | |||
ace3102601 | |||
48946100e9 | |||
0067e46192 | |||
921711a679 | |||
32dfb765dd | |||
8482f12909 | |||
306a56124c | |||
1f815d7562 | |||
f25d35fad8 | |||
f74c57449e | |||
a697bd935a | |||
ec294227bd | |||
f67758eaf3 | |||
f7ed65d749 | |||
7ffeb3964b | |||
025d4df774 | |||
45086a4b6e | |||
2db0023653 | |||
bfc21220a7 | |||
507491fbec | |||
c890ef6917 | |||
6756fb4fe7 | |||
6c089c0a78 | |||
b2ab3f987c | |||
c99b2edf98 | |||
e052610184 | |||
13f1725105 | |||
f2367932e1 | |||
7e94ec986e | |||
7bda3e6994 | |||
3a18606385 | |||
e25a94e815 | |||
c13f662e2d | |||
97ee085f30 | |||
1364fd5c45 | |||
cc3186a683 | |||
0c93c4754d | |||
7b4cfbeeaa | |||
8cebb53147 | |||
3e18f2f09c | |||
add09e52ef | |||
5429a509c6 | |||
3555fa36aa | |||
ee31519552 | |||
06b41aee58 | |||
cd19d50e1d | |||
a5e4eea5ca | |||
c6a6270e16 | |||
18d9d2602a | |||
f7ec9f2073 | |||
4c5f66185d | |||
105c1893cb | |||
061cee207f | |||
af4a925b54 | |||
a6f3e87921 | |||
9764d9109f | |||
46dfa57ee0 | |||
2c861c65d4 | |||
a59bac4b40 | |||
cf214bf367 | |||
75724797f7 | |||
d04aeb55ad | |||
47bd6dc6b8 | |||
5e0f525932 | |||
1f66daf2f3 | |||
ded9cb0358 | |||
04f201933b | |||
f5ec1cb3a4 | |||
6c23e3f534 | |||
e99d54d1f6 | |||
7f436061b8 | |||
3c71200eb4 | |||
f124cf8318 | |||
9d2b944063 | |||
8e1ec5903b | |||
5cf763d51f | |||
3546859fe5 | |||
6530e45178 | |||
07f0036b2b | |||
5237f55a71 | |||
a108e5067d | |||
a4a24b1a1a | |||
ffe0eb1544 | |||
288e8a65f3 | |||
0ebfbca93e | |||
f22f57495e | |||
8786a9d21d | |||
f06a97d30b | |||
2329c47faf | |||
2967261acb | |||
64ff1ecbb6 | |||
8707f88c07 | |||
36846618ec | |||
0cb2f19e29 | |||
125a50ae87 | |||
9d37ea23f8 | |||
31617ae340 | |||
950614fb81 | |||
14bbd7b7ae | |||
257cd34101 | |||
ab6ec3a9b7 | |||
39814a89b6 | |||
24fbbf8aa8 | |||
338ceffa6d | |||
371e104b00 | |||
d5aba8eaf1 | |||
1d2b3a4ed8 | |||
f904945d40 | |||
027b2e1b88 | |||
d79eb5e1a6 | |||
f6651b03b5 | |||
5f880a179c | |||
ea03fd22db | |||
e252c9ac05 | |||
a212fb35c1 | |||
e561e4de0b | |||
1c3d5cd851 | |||
e59fbac761 | |||
332f2b0678 | |||
745ea5fb05 | |||
fa16ca4eec | |||
d7757b8b03 | |||
98aefad249 | |||
a19ba40672 | |||
3983cb001f | |||
c17222dbe4 | |||
abd8c69395 | |||
a7fde73df4 | |||
78b464b404 | |||
aa21115e26 | |||
a39f845835 | |||
3fdd8d91e2 | |||
c13bccc7ae | |||
b4f7d6bf25 | |||
fa0c2f7138 | |||
453cc2a951 | |||
bd56795c62 | |||
2c54b7f289 | |||
cd5f847b55 | |||
a25544baea | |||
39b6c5d6f4 | |||
d1c9db874f | |||
f954542dda | |||
9fec7d236c | |||
67656accf8 | |||
64952a536c | |||
65e0d5f511 | |||
903acff924 | |||
5a06946469 | |||
baef31b2c7 | |||
b9a12d1562 | |||
3f26d03166 | |||
1fed3ad532 | |||
929b245f5f | |||
0da6354825 | |||
716a28891d | |||
93a2e91694 | |||
4913dc1aad | |||
087df18fea | |||
058ce6fe82 | |||
087c10d52d | |||
18292e447c | |||
6c1dda47c0 | |||
ad1fc8f3d8 | |||
bca98269bb | |||
1bebaf933d | |||
166eb996a9 | |||
10fae34754 | |||
aa4d97e8df | |||
dbbb9d7877 | |||
82fda5cb03 | |||
3ff213b3e8 | |||
65587536ab | |||
ad31be8344 | |||
25815c81bf | |||
852a22f86d | |||
69c7f22053 | |||
75a964167a | |||
c5768c81e1 | |||
4eb2b818e7 | |||
f742aad810 | |||
e22b171b7b | |||
d061eb2c64 | |||
69aa115178 | |||
e175b87384 | |||
f216ee739a | |||
16d6644573 | |||
38afc6e6f8 | |||
8f1d214b12 | |||
51fb1a43de | |||
a86b6bfbd6 | |||
1176ddcc85 | |||
fa080e380c | |||
57c3acd9d8 | |||
302cf5b10b | |||
e2a9e81dbc | |||
b1cf7391ce | |||
9bc7521de0 | |||
a68ebd2b76 | |||
47f7c938ae | |||
842e7e559e | |||
bd5a6e6fb3 | |||
67cca3bc00 | |||
90b1609d4e | |||
abbfae2fc0 | |||
b52b854270 | |||
58b759f652 | |||
74ca756a53 | |||
a62ee7850b | |||
d3a90ccc0d | |||
46b13ee664 | |||
cfa6dc7836 | |||
f969bfa7be | |||
3576214920 | |||
f964fe3750 | |||
e86a883d0a | |||
82d764000a | |||
749c72e6a6 | |||
c3129a40f1 | |||
d04aa89812 | |||
d5f854d376 | |||
63dcb8cfe1 | |||
6c57fa078b | |||
c3cc75feff | |||
d2e6011089 | |||
5a18144366 | |||
8a0a22bfb0 | |||
950b226374 | |||
74e64a4387 | |||
59e4c1cf79 | |||
9b89ede9c4 | |||
bf205de3a1 | |||
045ad78bb9 | |||
c0350e5be7 | |||
ea7006eec4 | |||
2b3e38f77e | |||
d04fe5d582 | |||
17ab4caa5e | |||
976bc727dd | |||
484e53cc08 | |||
b09b80933d | |||
8165086d02 | |||
82f14b087a | |||
93b3419737 | |||
19290fe467 | |||
d2f679030b | |||
053bce7a8e | |||
2f208832a9 | |||
268d7495cc | |||
ce16e61e63 | |||
f92bca58fa | |||
83d541b60d | |||
965efc3a13 | |||
d656c34bd4 | |||
7f151cbeba | |||
bc2f9204e9 | |||
a922a93016 | |||
eb596ba866 | |||
2208545612 | |||
f08a875cd2 | |||
d492d3f738 | |||
c687091ce9 | |||
eb994716e6 | |||
70acc8a7c0 | |||
bf97781232 | |||
099727d671 | |||
6229cdb1ba | |||
b7a663ed20 | |||
3bd97352ba | |||
33e25d9241 | |||
fc11018158 | |||
5e22360cb1 | |||
840348b4eb | |||
450fb2553c | |||
cf04738594 | |||
03757632cf | |||
e818f5a93f | |||
ab9b08770a | |||
40df8b68ad | |||
9f5202fee3 | |||
902ccbd203 | |||
4675da4d16 | |||
86da27a7a1 | |||
fc2a6567da | |||
7c611d9b62 | |||
784c7465d1 | |||
301af7bd7a | |||
09c11a385d | |||
ef6f491d94 | |||
9dcef00fbb | |||
e781e5dd43 | |||
d3e672d811 | |||
dad1554ec2 | |||
30bf96c6cd | |||
a8c16e39b8 | |||
79a7cd2938 | |||
26562e445f | |||
0b678b1f16 | |||
79b5e85b15 | |||
2432491bfc | |||
a09ce3e026 | |||
c52fc843f6 | |||
02240bda25 | |||
0185ef7c83 | |||
7d29b9901c | |||
ae553dfed3 | |||
71c6beadb4 | |||
d939629c09 | |||
0a569146a8 | |||
d5a012d49f | |||
22a11769fa | |||
7dc7ba9977 | |||
fa4059a4b9 | |||
7f4786f9dd | |||
5a6e7a46d1 | |||
9f90749f99 | |||
0dfaf9159d | |||
5d8bda1178 | |||
9ad1e0d529 | |||
389e3397ec | |||
284b95213e | |||
952854f64e | |||
554650c18d | |||
01a2fa7c2d | |||
1257e34487 | |||
a45743f443 | |||
7d03719816 | |||
7c5bbca2fa | |||
cf313939aa | |||
873d4bd3f2 | |||
f43f3fc84b | |||
0e1fed86ba | |||
3fb5d886dc | |||
b57cd8d5c2 | |||
5c1bbc08ca | |||
6ba32b95f3 | |||
d3df113fb0 | |||
06c2ab045a | |||
6d43e0951c | |||
ec14429238 | |||
1dc2c6f183 | |||
984b8f7e6f | |||
0f8448b2c0 | |||
088c546bee | |||
20fff378f0 | |||
4994a7ac85 | |||
a959c69d32 | |||
39244568be | |||
c8fc0bb4f5 | |||
654ad5c71f | |||
7b9d18caea | |||
8d347efec2 | |||
85de6dd52e | |||
c024b39c8b | |||
1f0db48487 | |||
2bc5f475f4 | |||
5abf4c99de | |||
137e519b66 | |||
dcb27e7de8 | |||
d4d5b5a75c | |||
a83b3f8408 | |||
da9af8673f | |||
1b4ba3b396 | |||
eb2f1cbc9e | |||
28f58f72dd | |||
81389401df | |||
c618c5c5f0 | |||
0eaff4c626 | |||
9783c1052d | |||
f732fa9736 | |||
0c2d227da1 | |||
a281efef04 | |||
538dcec348 | |||
0d38c8ae8f | |||
967c1a2da9 | |||
f3da326b77 | |||
153a6e2cb0 | |||
95f37b9d36 | |||
60c37a1fc7 | |||
615c61e230 | |||
ae40b6ba8c | |||
d482427e0d | |||
c41baf3aeb | |||
dd7cb74edc | |||
4eed2c7582 | |||
100e830e04 | |||
af28d82ebc | |||
6285980f98 | |||
a5d19cd31f | |||
9c9998b468 | |||
011eb55a53 | |||
189d31cc29 | |||
d178f3d1b9 | |||
6e9d73ec64 | |||
8d1adf4f80 | |||
d0b7f58e7c | |||
19d24e5644 | |||
461f618b8a | |||
fc875651d3 | |||
5ff14d1fed | |||
80c9c1bb05 | |||
a111d9b18a | |||
df14913c67 | |||
b6c6fef770 | |||
1a2f37b0ec | |||
6f3c662783 | |||
3772137c8f | |||
0d62123a0b | |||
28fed6281f | |||
1ec95d42ba | |||
8adf965d0b | |||
026dd38480 | |||
338c2243e3 | |||
e8d61225f5 | |||
cc356ce67d | |||
364e364429 | |||
46a46877ed | |||
5ee05e3aaa | |||
5568a09f49 | |||
1199c431ff | |||
2c1a897c4e | |||
305f2fa448 | |||
b051685727 | |||
344dd92c85 | |||
4167c65acf | |||
cd6d49860f | |||
8a10fcf7ea | |||
7580bb21c3 | |||
62102236a2 | |||
3b5f96a133 | |||
ce2b711b1f | |||
667fb438cb | |||
7befa94e6d | |||
32d7835119 | |||
726abf6e65 | |||
c154a4bdc8 | |||
88ef1a3c5b | |||
5453925e26 | |||
537e314b49 | |||
ccb7a553c2 | |||
1696a5c8e1 | |||
816cf8f702 | |||
e8167541af | |||
329360aa5b | |||
eb1a276e60 | |||
a53bac1a94 | |||
93bf93d3a1 | |||
4174c8c25c | |||
48a88a8624 | |||
1442748f58 | |||
d17e216f91 | |||
56ed4fe6f2 | |||
9a71e9ba86 | |||
ef478a4a9e | |||
1bd7d40716 | |||
807e9573fb | |||
849d1d7ebd | |||
3fe2545228 | |||
090dfff730 | |||
f94e9449d5 | |||
d8753adc4e | |||
2e17ea99e2 | |||
c4daf8524e | |||
9d16898926 | |||
f4bcc1f2e5 | |||
63e8614ace | |||
5d686b146d | |||
e85758dc5f | |||
d08f090800 | |||
8554473c21 | |||
01fb1bde8b | |||
29e32ffc42 | |||
88bd60a083 | |||
48b7b725b0 | |||
8d8c932d8c | |||
253d355bd2 | |||
e287df1320 | |||
bae0bec1cc | |||
602686a5d2 | |||
af05d94198 | |||
5fa3a7ca44 | |||
9609350789 | |||
9c1e73ffff | |||
50741c70c0 | |||
fc8660df78 | |||
4e5ddca3bd | |||
3bdc90451a | |||
a036b2981a | |||
8fae83dab7 | |||
083f9dd29b | |||
7d5fabbd25 | |||
105f071847 | |||
ef68e5b13d | |||
21afe077d7 | |||
48222ce44c | |||
0922ba938c | |||
3fc66ec525 | |||
44191cd908 | |||
0da0c6bd77 | |||
b5f6e9d01b | |||
6098b196dc | |||
0922349344 | |||
53cdeeff03 | |||
fcdb086daf | |||
d2d9c2dd0f | |||
4241fb9386 | |||
cfd6751777 | |||
5e461e9b6b | |||
946dfdf7b8 | |||
4da9843479 | |||
eccb3c643d | |||
bfa5a51ce8 | |||
9066ad6cdf | |||
f7b513dff2 | |||
3de5f10d52 | |||
07429a862c | |||
2f2bddf020 | |||
ad03adaebf | |||
ac7a5488ee | |||
6de93d4fbb | |||
8a312f76c5 | |||
4a956b5a55 | |||
83d6c3ba88 | |||
52daf6b864 | |||
c8420e152f | |||
1dff19af26 | |||
a1e2eca802 | |||
48b63a26c8 | |||
b23bc4a5b6 | |||
940236b4a4 | |||
b6ef18b0d8 | |||
372484f976 | |||
086cf67e93 | |||
3c8692d06c | |||
efffca83fe | |||
04fe81e001 | |||
d2215c2ba9 | |||
89b1b6e242 | |||
e476d68848 | |||
da8835bc77 | |||
f170c2611c | |||
351b17d1d9 | |||
14e88706df | |||
926e3e2712 | |||
c39043fb9d | |||
578b3ba4f4 | |||
5b0b582039 | |||
e24be913e5 | |||
4d3358ba66 | |||
ffe40fa3a3 | |||
87f93b34a3 | |||
03bd9a5731 | |||
cb82170187 | |||
5b9e16af83 | |||
7b1b2a4bef | |||
1f1a0b7b53 | |||
320acfae89 | |||
1c171d0f12 | |||
22bf3618ea | |||
5f1593f4d0 | |||
9af75bf9b2 | |||
344fa729a5 | |||
33d3d90a93 | |||
24dfc09f35 | |||
e533bc0847 | |||
306333ceba | |||
e96312b470 | |||
fb60b4bca7 | |||
0612e4429d | |||
ee80aa26db | |||
a45e667e9c | |||
1b4a2369bb | |||
224483f6ac | |||
c61574b782 | |||
c92129ac63 | |||
6c71d95932 | |||
e859f5c7a1 | |||
dc402f5f0e | |||
188894c837 | |||
70f99a70a5 | |||
fb4fbd23d8 | |||
f58b2383b9 | |||
05caf1fe28 | |||
704486abc2 | |||
1ec023b435 | |||
fd21eeb477 | |||
edf2b2df6f | |||
9ce338622c | |||
2b35529cbd | |||
554b67a2f0 | |||
012243a880 | |||
d4a348a2b2 | |||
1d4c5cc96f | |||
41bfb96b6b | |||
994d62ac65 | |||
7c72608e1c | |||
2edc06c662 | |||
cb4d66d6fe | |||
f80602b51a | |||
58d8a5ce46 | |||
72a65218be | |||
1b0d5b710e | |||
2a25ac0847 | |||
9aefdf35a1 | |||
231961c017 | |||
ee621fa091 | |||
a69a04cfb6 | |||
b1aed344c7 | |||
3e08d665c7 | |||
4a94c86433 | |||
d4878f6ed3 | |||
d94719ea02 | |||
982b5221b1 | |||
cbdf03450d | |||
7625e591fe | |||
8fdb1e7ec9 | |||
d3b28c42e6 | |||
1b32423881 | |||
7de699c7fa | |||
e9f9670eb5 | |||
3d4e961320 | |||
db2fb33d53 | |||
ff3db04ab7 | |||
c7f6763c48 | |||
b1dd4069db | |||
58c647d433 | |||
333ea4aa53 | |||
3ad59da2a9 | |||
2d9b211eeb | |||
4f5a352985 | |||
6ae3b77c2f | |||
4a7260b1be | |||
f91c77bdc6 | |||
476e938d23 | |||
1ec9d986bb | |||
4b88cfa51a | |||
bc56226a28 | |||
a6e5474fdb | |||
8c7ca2c34d | |||
91fccc6691 | |||
93d1737357 | |||
5ba1ae9ae4 | |||
8cb408bc6e | |||
197a89a37a | |||
d336ead3b1 | |||
662644663e | |||
4c7819effb | |||
8b5b9ee8f1 | |||
89b911a9dc | |||
b673e216b6 | |||
a1b2f0ccf1 | |||
f269facf9d | |||
5a36d280d7 | |||
c39563b123 | |||
548149de8e | |||
d6d4ce0ac4 | |||
83b0239791 | |||
d1fa13d67a | |||
3abd570678 | |||
b0b0781bd7 | |||
1aa28ddee1 | |||
09b50badb1 | |||
7060108a8b | |||
e6f0d5bf44 | |||
399642f958 | |||
781effc34e | |||
324c8f8146 | |||
27e372e38f | |||
87122ce211 | |||
c0c6675423 | |||
fa4aeb5261 | |||
4e51eeb998 | |||
a27c3f09b3 | |||
3e5f117066 | |||
3753fb3ea4 | |||
d3e49cf1e9 | |||
ffcf46a371 | |||
fc5eedbef5 | |||
d78b6c4445 | |||
6e056bb337 | |||
b5c2c1009c | |||
b54029a04a | |||
e30aca7531 | |||
866722b68f | |||
d93f3468d3 | |||
83032e858a | |||
6855e314b3 | |||
2c4a8619a8 | |||
3247d83252 | |||
109ff2d8a5 | |||
48797d12eb | |||
abe66d8af0 | |||
58c9b70b26 | |||
996643bde8 | |||
68dd9a29e8 | |||
68c4b55945 | |||
bab86c9ce2 | |||
5f24e4d705 | |||
66c7b3fcb2 | |||
1ffe29c657 | |||
6b7d4877e6 | |||
80826eb500 | |||
11962facde | |||
c7eccfd804 | |||
ad617e0deb | |||
7ae70d5a4d | |||
62d1a0291e | |||
883dc72fc6 | |||
65b2e9633c | |||
3b923e0d37 | |||
4938cb9bbc | |||
a208564f06 | |||
6c6ca4daf4 | |||
1024da601d | |||
55d05ee590 | |||
66f39e070b | |||
252681001e | |||
a39014b3b1 | |||
10bcfdd6b2 | |||
41ea9814e8 | |||
bccf7e3f69 | |||
06c8a004d8 | |||
4ab90fb14d | |||
54d8feeaf0 | |||
23d20c918f | |||
10012d7125 | |||
d70ea854b1 | |||
8c576ece28 | |||
e0c0935d3a | |||
ab987e73c6 | |||
cec203a6db | |||
cf8bb9efb0 | |||
201a053025 | |||
703f987825 | |||
4e3d677d8e | |||
aab90130d2 | |||
9321dfdd89 | |||
2607ef5fe0 | |||
84b291a57a | |||
3271c5a60b | |||
79bb66f535 | |||
1616885e7f | |||
85045c3e9c | |||
4a85296e23 | |||
4710d18e33 | |||
3da75494b0 | |||
ac021f38da | |||
d0a2c36f6c | |||
9facc9379f | |||
a0f4e2659b | |||
4dfa3d8372 | |||
30563ee04c | |||
3fa7594fcc | |||
deac32d843 | |||
ccfa32ce93 | |||
f05d114f05 | |||
fcb41e30dc | |||
1cdb456216 | |||
5ed6bba412 | |||
50e929eb0c | |||
129839ce21 | |||
fd2961ecb9 | |||
e506d988e2 | |||
196649c0e9 | |||
12182ee04f | |||
5db64526cc | |||
5c2ec70eb4 | |||
24a2c6251f | |||
0d035d9ae9 | |||
a28f1294e2 | |||
a3b0cde59d | |||
c2dec9eac4 | |||
0fdb49f168 | |||
e86d02765f | |||
6e877aea02 | |||
330f1577fd | |||
ff980591c3 | |||
60992ae492 | |||
1b439c15f6 | |||
f58c16ab03 | |||
b560d2953f | |||
2c52c8efb7 | |||
5375166f04 | |||
5e85cae9ce | |||
0ae449fd44 | |||
4f5b4f387a | |||
fcd3c39656 | |||
b4c68a8ae5 | |||
a8d3d613b0 | |||
c680fbe834 | |||
34023f66f0 | |||
2696da7337 | |||
4edbd8719c | |||
ed6c59b58a | |||
553525a0fb | |||
38ebb95e63 | |||
17d92d8b79 | |||
4c08c692ea | |||
286c77778f | |||
10c4bee1e5 | |||
f26b8ee224 | |||
1961567ebd | |||
094687717d | |||
e5844c926b | |||
96ad8c823a | |||
de2977284c | |||
32bafd8b01 | |||
c1dea44fa6 | |||
edb4b9a82e | |||
008b4228d2 | |||
5a105eb2b3 | |||
faf455a37c | |||
7a076f7304 | |||
828a7aabd7 | |||
acbf2a8ab0 | |||
05a4701d98 | |||
6a19b333b7 | |||
864fc84899 | |||
cddd62c284 | |||
30c46c0858 | |||
396e54bad0 | |||
a2b9ee5988 | |||
fac8e8aa8b | |||
a821b309f7 | |||
bd2b627113 | |||
3c3711b933 | |||
926ffefac8 | |||
986b303f2f | |||
afd8717c21 | |||
6effd783c0 | |||
b8f47545ed | |||
a10c4f7a34 | |||
939dd0b207 | |||
defabf4324 | |||
a6394b2dce | |||
5ba802dc68 | |||
b652d0fc65 | |||
e67843638f | |||
ca704e1d51 | |||
04eb356c89 | |||
300159c03b | |||
f083b816a9 | |||
2a46f6b225 | |||
b3e9b266fa | |||
76c72c1a7f | |||
1c1cf58409 | |||
8155e3ef7a | |||
d9fa2c4a62 | |||
547e640b57 | |||
e107037011 | |||
62048c68f0 | |||
489aae7a46 | |||
1bcb6738bb | |||
d14a13fcaf | |||
5facba4419 | |||
f32df2ac9c | |||
22a0b3be45 | |||
b4282a03ca | |||
3490d0d743 | |||
688659b815 | |||
37cf9eb587 | |||
65b2da4db5 | |||
2cecb11847 | |||
fbe829def7 | |||
a39e0a19cd | |||
50d805abbc | |||
b7467b466f | |||
3048a26e6f | |||
0175d7658e | |||
5eee528d6b | |||
4a431ddc7c | |||
ebda485bcb | |||
f9bb55bc5c | |||
5d207f36e6 | |||
6fc9098035 | |||
215c3d82e2 | |||
86d0feed32 | |||
53674b03fc | |||
102e454902 | |||
88d59eb7fa | |||
53ebd583cf | |||
a1914f5079 | |||
5231eb62e1 | |||
661cf440f3 | |||
3822058daf | |||
011f283067 | |||
4f58a485a6 | |||
858a1c7ae0 | |||
0c96510128 | |||
ecf7860847 | |||
161eea3e9e | |||
27e2699fa1 | |||
906d90c304 | |||
d243bf069e | |||
dde3d5c35b | |||
d1e792686e | |||
62fe380520 | |||
319f08c4c9 | |||
657129e4a7 | |||
5bbef09f85 | |||
342f5e5e41 | |||
02c0bac71f | |||
9bb091d31e | |||
a0e201a9ef | |||
119d5ba7ff | |||
a35454b1b3 | |||
8cb340be9d | |||
8d21ee23f4 | |||
5e7c376950 | |||
7617d56276 | |||
80e4b33047 | |||
4f6287c163 | |||
84ee1a2d25 | |||
67252e0c6b | |||
4264c2e266 | |||
0efa4ffb23 | |||
0d13fe67b0 | |||
ae108bb603 | |||
ca4229c732 | |||
5c827703a1 | |||
a3de0820b3 | |||
83cb473a45 | |||
e7f9db5e56 | |||
af3de448bd | |||
fcb2f1b555 | |||
c1bcf9fa8a | |||
5ddfe7a184 | |||
c675caf3f9 | |||
956b3d89db | |||
a0c6e9e490 | |||
b0c15bc430 | |||
56960d6da9 | |||
b934a6b6e9 | |||
df73be495e | |||
efca13c8c0 | |||
e49b0b9381 | |||
a69cd85ed7 | |||
7b520942dc | |||
7ee00230fd | |||
bc8c7285da | |||
12e6baa925 | |||
f98225cdb6 | |||
310abcd8b9 | |||
f42eaaea86 | |||
bfdb74979f | |||
2590dc690e | |||
e2545b3d34 | |||
28e002e248 | |||
7d12dbd4b2 | |||
6f60e71ea4 | |||
16c842366a | |||
97ba151e09 | |||
18f452d968 | |||
bb6db783d8 | |||
49ce302bd4 | |||
95f01007a3 | |||
bf4d920fb4 | |||
d84fb244d3 | |||
f04b5244fa | |||
6f3829fa91 | |||
a5ae3545d5 | |||
63b658ac50 | |||
e8544be30a | |||
39090451f0 | |||
12fc6e6354 | |||
154326061d | |||
0bbfd574b3 | |||
eff3887f35 | |||
34cb532e6b | |||
cf1d1a97f3 | |||
e0a4f0bc4c | |||
1ac8eba1b8 | |||
82f8c31b81 | |||
9f2ae75fa2 | |||
9d220c482d | |||
c4718bd302 | |||
e1e2a90498 | |||
40a48954c3 | |||
e81068f528 | |||
0591487cfd | |||
737ed7ba5d | |||
27fd1f39f0 | |||
1210313e7f | |||
4733c8fb75 | |||
8777e65996 | |||
330fa10935 | |||
1bd4a0f4ee | |||
bea8ae55de | |||
b99cd49091 | |||
35f3b70968 | |||
e33f3231d0 | |||
b6cf6ee94a | |||
6118518b1b | |||
f77d5ebfd2 | |||
fcf5c41709 | |||
7f3279f20f | |||
c23de50f3e | |||
51f1759323 | |||
cedc634933 | |||
c7b89a1126 | |||
bad015de9a | |||
21057759de | |||
d027352d79 | |||
fd8c568d75 | |||
85292ca1b4 | |||
1ab90e76d1 | |||
1e9e9daeb3 | |||
65682a8130 | |||
bca225e692 | |||
4fbc46b3b3 | |||
4b3806fb9a | |||
5abbec94c4 | |||
346d1dddba | |||
8b48ad77e8 | |||
64d97a3232 | |||
94a208bd91 | |||
b15ac8b13e | |||
0ddff9d551 | |||
7c31dbb3d6 | |||
6ee995afaa | |||
691acb647e | |||
80d9d88de1 | |||
cf12abfc7f | |||
108e516b80 | |||
b21ec7a302 | |||
972473f1af | |||
ee8e0b46a2 | |||
1c3d270402 | |||
9d09d2042a | |||
b8823ade29 | |||
0e5b01f897 | |||
87f221290f | |||
fd3b0c20b6 | |||
93e71027fd | |||
1c5097b81b | |||
ef1bbda49c | |||
c6852c70fd | |||
953773aaa8 | |||
e0a139d083 | |||
867140df30 | |||
8a354e6187 | |||
5fed14cb78 | |||
7e7c110851 | |||
5337153761 | |||
444834a891 | |||
2587727087 | |||
a6456b068d | |||
4ccf26c23f | |||
3927dfa71d | |||
31c324bcc3 | |||
476d6fe85d | |||
ee4d468bce | |||
47fca55b0c | |||
6afd4b0dc7 | |||
e2517ef50e | |||
ad6798eaad | |||
2a5fcc0846 | |||
37592a876e | |||
4979e176c7 | |||
f3fdac2762 | |||
9a67617cef | |||
96e43cbb65 | |||
458dc5b232 | |||
28a7c6d3aa | |||
d3c241d283 | |||
445959abbd | |||
62842968cb | |||
2dda683aa6 | |||
616167e08c | |||
acd74c60c8 | |||
c1c4a2933e | |||
8158ead1a2 | |||
fcc196f452 | |||
d55e2492d4 | |||
45e05b0891 | |||
eb53739c95 | |||
219f310a25 | |||
4a35059711 | |||
27071cfa29 | |||
a1ad1147e6 | |||
eefa7d53c5 | |||
716491a68e | |||
29e5263fcc | |||
4df61d7efc | |||
34aa60d47b | |||
1d88a5b42e | |||
74a9c46f08 | |||
7a540f2a88 | |||
9f48d5e5ff | |||
64cc2567bd | |||
3b47eb3b07 | |||
d13e08e53b | |||
4685461282 | |||
885759abc5 | |||
0c0c8e503e | |||
5605cfe213 | |||
3e3fc54da4 | |||
e59c66ae26 | |||
d74eef8088 | |||
88a240b0f6 | |||
ee21f41b25 | |||
9ec2010ac2 | |||
e928fee26f | |||
79f6723678 | |||
db1fbad0db | |||
812a0a14fc | |||
852875b440 | |||
d1dd1b8a9b | |||
30974482c5 | |||
982696fb3b | |||
cf43dc7b5c | |||
4444525c01 | |||
760cc89449 | |||
ba26f22f53 | |||
e1f37a2f3c | |||
e59287d736 | |||
5cda0c7684 | |||
717a1d8f57 | |||
a955730086 | |||
d5ccee7bbb | |||
97e2a17ce1 | |||
a32a58bd0f | |||
093201ef65 | |||
ef46603f4e | |||
3483f63b72 | |||
8bee8060f9 | |||
db58c9aca9 | |||
75f5ec8575 | |||
246ceebe0e | |||
a294b128c7 | |||
7879bf19eb | |||
80082d9c26 | |||
9fe1709bf7 | |||
fec21f1208 | |||
1d4b34c0dd | |||
d88e0f16ac | |||
8d31c474df | |||
704d545159 | |||
a1e5a2cb67 | |||
3668c87e0d | |||
28c8dc4bcc | |||
a02915dadb | |||
c916f46ac3 | |||
5230fbaf6e | |||
813e65e586 | |||
387605f443 | |||
ff335ecadd | |||
74ccee2aa4 | |||
4976f35979 | |||
9c09a4d393 | |||
905bcd8d1b | |||
6883618825 | |||
8beaf2107e | |||
cd1db214b0 | |||
a6b4d59d94 | |||
ff590d3090 | |||
c16e425980 | |||
622322c878 | |||
7c580e276a | |||
927013cd57 | |||
666bf0ebb4 | |||
c283d3181f | |||
28dfe2140c | |||
75ac3450e6 | |||
f727dd26ed | |||
d21c0bfc18 | |||
65b2c056c6 | |||
53533e71e9 | |||
90a28732af | |||
98b1e50c86 | |||
512ffa9030 | |||
dbd37a0961 | |||
4eaba01de0 | |||
09cdbe6b90 | |||
0d33964a03 | |||
b14523ecfa | |||
38fa083503 | |||
fff050ef14 | |||
1a10c60e4f | |||
eb6d19a4dc | |||
9d92174b1d | |||
0dd38870e0 | |||
964d752e11 | |||
a4305540f0 | |||
788dcbf471 | |||
a715022049 | |||
c9fe2e8a66 | |||
5170f508f7 | |||
72900eaf93 | |||
85b6540c9f | |||
d92fb1ec95 | |||
9051bf6e68 | |||
598de3697d | |||
ff515f9bb0 | |||
10ed23e144 | |||
253e75c747 | |||
6efbe62dca | |||
0eae17075f | |||
c7c47635f7 | |||
8e1445d27a | |||
0fc92942cf | |||
2628a061f7 | |||
78971ac504 | |||
107a0e1b7d | |||
fc6954a541 | |||
2f60afb039 | |||
1a2da16e12 | |||
678e0912ae | |||
8d596c07df | |||
dc9f6013e8 | |||
e5a21fda32 | |||
2a0f920bcd | |||
dbd2f2003d | |||
4922877816 | |||
e914378dd9 | |||
9ec35d2bfe | |||
d96cc79814 | |||
7b92703624 | |||
22089c528a | |||
20ebdc46e9 | |||
3224cd73ed | |||
024c7f6a15 | |||
96d4b52da3 | |||
d1e29b8a9d | |||
0e02714114 | |||
04ad3c0386 | |||
14985bcdcd | |||
a1908de302 | |||
8820f10e01 | |||
e1116938ec | |||
c30678af98 | |||
1d4e06b884 | |||
39d6fcac73 | |||
f44c1d4536 | |||
c4349951da | |||
5a42704ac4 | |||
52e94fe650 | |||
466b9da56c | |||
b280288e83 | |||
7388c13c63 | |||
b4f4ccec99 | |||
e5e3f02440 | |||
874bfa0c54 | |||
92a4f7adb8 | |||
0772417f33 | |||
14f1b6df4b | |||
0d9c8a804d | |||
0dfbfafb82 | |||
051ef564e7 | |||
bafd3612e6 | |||
05f9f7ce9d | |||
88acf49305 | |||
74d9901ec9 | |||
64d1b56497 | |||
0cc540b12a | |||
a1712a654d | |||
fe21889ab0 | |||
e50c84ff28 | |||
44824acf34 | |||
ec3253620e | |||
f902466882 | |||
b5d2a23c64 | |||
0fd4804f95 | |||
729d0ea417 | |||
8f2f644230 | |||
183edf9eaf | |||
46c37403a6 | |||
c57e15bdf1 | |||
bc0e53a59b | |||
2c38b51996 | |||
67e36788b0 | |||
9b8ed32c74 | |||
833063c916 | |||
3b2c8e0a97 | |||
ee90d1f258 | |||
f90f42c25c | |||
d6555cb344 | |||
286f057a14 | |||
00b89c2bc7 | |||
b4a3de4cff | |||
ec49c96219 | |||
1eb420bcda | |||
9ba810ccb6 | |||
7a99241c76 | |||
617066c7e1 | |||
835dd4da9d | |||
15da928655 | |||
7460e6f73b | |||
4104b4d0a3 | |||
a29259a8b6 | |||
8925c6caf9 | |||
7b2a85a118 | |||
f8980aecf0 | |||
a4f44f02ed | |||
3fe76a6bd3 | |||
8c060b468b | |||
6136039682 | |||
f2954eeb3c | |||
f186c41f4d | |||
7d69f4d3ed | |||
4baec1b185 | |||
90c4361363 | |||
10aaa48068 | |||
458b0150ef | |||
44a3f63f99 | |||
96a6b11ab4 | |||
9ef9ce76f8 | |||
94835c46a0 | |||
58b3d31526 | |||
b023f5c0da | |||
78b87c6ddd | |||
edde4dc2fa | |||
d9a6e41265 | |||
64f0f1aa2c | |||
b54c057c83 | |||
baa4acaf79 | |||
3c6bb41939 | |||
3f64d3729e | |||
21dc2ece1b | |||
fcac6c4f8c | |||
e5dc932717 | |||
6b65b05e2f | |||
d52b973b44 | |||
79d3f4ca9e | |||
06dd22d89a | |||
e79e425cf5 | |||
b4b2c351b4 | |||
73acaadf70 | |||
18ef36bbc3 | |||
021315f0f5 | |||
a4ee103ff0 | |||
618173c5f0 | |||
4cb906571b | |||
6092b6c4cf | |||
9bf17a1c8d | |||
542379dcf4 | |||
a565bb5889 | |||
9a96ff2edc | |||
9fa2e363cc | |||
a9939a31cf | |||
64fffbcdec | |||
a65f8f5d5c | |||
c5475fb028 | |||
f267c46595 | |||
b1f67a9a65 | |||
044a7524a3 | |||
495b15e065 | |||
f4e6c399f2 | |||
4519acb77e | |||
cf1ba6d459 | |||
c28cb67484 | |||
9017ee9a40 | |||
52086a2d39 | |||
facec59fe8 | |||
75eb79bd55 | |||
307209945c | |||
8db9f40001 | |||
472b8d0e51 | |||
dbebd32a6e | |||
ad8d2a913b | |||
38bd247d64 | |||
6a4e972de6 | |||
cc14ac0bac | |||
2e0d7fdbb8 | |||
2284eea2d8 | |||
867b5b2ee4 | |||
85e29fffc9 | |||
fa84d812f1 | |||
4ffa8420dd | |||
7dc1c54578 | |||
05434d3575 | |||
ddea9b9f38 | |||
9445ee41cf | |||
8325b4e5aa | |||
1ff9db3714 | |||
12ff102a21 | |||
f660111751 | |||
bac08306fb | |||
b860dbd9a6 | |||
c7713f559d | |||
7c60189f29 | |||
35dc13ffcf | |||
6b55f385c7 | |||
75a9466232 | |||
2ffe7cc843 | |||
d111911c18 | |||
d55935f8c0 | |||
4def55368c | |||
661eff90fc |
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
ko_fi: cmdr2_stablediffusion_ui
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS:
|
||||
- Browser:
|
||||
- Version:
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device:
|
||||
- OS:
|
||||
- Browser
|
||||
- Version
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
4
.gitignore
vendored
@ -1 +1,5 @@
|
||||
__pycache__
|
||||
installer
|
||||
installer.tar
|
||||
dist
|
||||
.idea/*
|
||||
|
27
3rd-PARTY-LICENSES
Normal file
@ -0,0 +1,27 @@
|
||||
jquery-confirm
|
||||
==============
|
||||
https://craftpip.github.io/jquery-confirm/
|
||||
|
||||
jquery-confirm is licensed under the MIT license:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Boniface Pereira
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
134
CHANGES.md
Normal file
@ -0,0 +1,134 @@
|
||||
# What's new?
|
||||
|
||||
## v2.5
|
||||
### Major Changes
|
||||
- **Nearly twice as fast** - significantly faster speed of image generation. We're now pretty close to automatic1111's speed. Code contributions are welcome to make our project even faster: https://github.com/easydiffusion/sdkit/#is-it-fast
|
||||
- **Full support for Stable Diffusion 2.1 (including CPU)** - supports loading v1.4 or v2.0 or v2.1 models seamlessly. No need to enable "Test SD2", and no need to add `sd2_` to your SD 2.0 model file names. Works on CPU as well.
|
||||
- **Memory optimized Stable Diffusion 2.1** - you can now use Stable Diffusion 2.1 models, with the same low VRAM optimizations that we've always had for SD 1.4. Please note, the SD 2.0 and 2.1 models require more GPU and System RAM, as compared to the SD 1.4 and 1.5 models.
|
||||
- **11 new samplers!** - explore the new samplers, some of which can generate great images in less than 10 inference steps! We've added the Karras and UniPC samplers.
|
||||
- **Model Merging** - You can now merge two models (`.ckpt` or `.safetensors`) and output `.ckpt` or `.safetensors` models, optionally in `fp16` precision. Details: https://github.com/cmdr2/stable-diffusion-ui/wiki/Model-Merging
|
||||
- **Fast loading/unloading of VAEs** - No longer needs to reload the entire Stable Diffusion model, each time you change the VAE
|
||||
- **Database of known models** - automatically picks the right configuration for known models. E.g. we automatically detect and apply "v" parameterization (required for some SD 2.0 models), and "fp32" attention precision (required for some SD 2.1 models).
|
||||
- **Color correction for img2img** - an option to preserve the color profile (histogram) of the initial image. This is especially useful if you're getting red-tinted images after inpainting/masking.
|
||||
- **Three GPU Memory Usage Settings** - `High` (fastest, maximum VRAM usage), `Balanced` (default - almost as fast, significantly lower VRAM usage), `Low` (slowest, very low VRAM usage). The `Low` setting is applied automatically for GPUs with less than 4 GB of VRAM.
|
||||
- **Find models in sub-folders** - This allows you to organize your models into sub-folders inside `models/stable-diffusion`, instead of keeping them all in a single folder.
|
||||
- **Save metadata as JSON** - You can now save the metadata files as either text or json files (choose in the Settings tab).
|
||||
- **Major rewrite of the code** - Most of the codebase has been reorganized and rewritten, to make it more manageable and easier for new developers to contribute features. We've separated our core engine into a new project called `sdkit`, which allows anyone to easily integrate Stable Diffusion (and related modules like GFPGAN etc) into their programming projects (via a simple `pip install sdkit`): https://github.com/easydiffusion/sdkit/
|
||||
- **Name change** - Last, and probably the least, the UI is now called "Easy Diffusion". It indicates the focus of this project - an easy way for people to play with Stable Diffusion.
|
||||
|
||||
Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed.
|
||||
|
||||
### Detailed changelog
|
||||
* 2.5.22 - 28 Feb 2023 - Minor styling changes to UI buttons, and the models dropdown.
|
||||
* 2.5.22 - 28 Feb 2023 - Lots of UI-related bug fixes. Thanks @patriceac.
|
||||
* 2.5.21 - 22 Feb 2023 - An option to control the size of the image thumbnails. You can use the `Display options` in the top-right corner to change this. Thanks @JeLuf.
|
||||
* 2.5.20 - 20 Feb 2023 - Support saving images in WEBP format (which consumes less disk space, with similar quality). Thanks @ogmaresca.
|
||||
* 2.5.20 - 18 Feb 2023 - A setting to block NSFW images from being generated. You can enable this setting in the Settings tab.
|
||||
* 2.5.19 - 17 Feb 2023 - Initial support for server-side plugins. Currently supports overriding the `get_cond_and_uncond()` function.
|
||||
* 2.5.18 - 17 Feb 2023 - 5 new samplers! UniPC samplers, some of which produce images in less than 15 steps. Thanks @Schorny.
|
||||
* 2.5.16 - 13 Feb 2023 - Searchable dropdown for models. This is useful if you have a LOT of models. You can type part of the model name, to auto-search through your models. Thanks @patriceac for the feature, and @AssassinJN for help in UI tweaks!
|
||||
* 2.5.16 - 13 Feb 2023 - Lots of fixes and improvements to the installer. First round of changes to add Mac support. Thanks @JeLuf.
|
||||
* 2.5.16 - 13 Feb 2023 - UI bug fixes for the inpainter editor. Thanks @patriceac.
|
||||
* 2.5.16 - 13 Feb 2023 - Fix broken task reorder. Thanks @JeLuf.
|
||||
* 2.5.16 - 13 Feb 2023 - Remove a task if all the images inside it have been removed. Thanks @AssassinJN.
|
||||
* 2.5.16 - 10 Feb 2023 - Embed metadata into the JPG/PNG images, if selected in the "Settings" tab (under "Metadata format"). Thanks @patriceac.
|
||||
* 2.5.16 - 10 Feb 2023 - Sort models alphabetically in the models dropdown. Thanks @ogmaresca.
|
||||
* 2.5.16 - 10 Feb 2023 - Support multiple GFPGAN models. Download new GFPGAN models into the `models/gfpgan` folder, and refresh the UI to use it. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - Allow a server to enforce a fixed directory path to save images. This is useful if the server is exposed to a lot of users. This can be set in the `config.json` file as `force_save_path: "/path/to/fixed/save/dir"`. E.g. `force_save_path: "D:/user_images"`. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - The "Make Images" button now shows the correct amount of images it'll create when using operators like `{}` or `|`. For e.g. if the prompt is `Photo of a {woman, man}`, then the button will say `Make 2 Images`. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - A bunch of UI-related bug fixes. Thanks @patriceac.
|
||||
* 2.5.15 - 8 Feb 2023 - Allow using 'balanced' VRAM usage mode on GPUs with 4 GB or less of VRAM. This mode used to be called 'Turbo' in the previous version.
|
||||
* 2.5.14 - 8 Feb 2023 - Fix broken auto-save settings. We renamed `sampler` to `sampler_name`, which caused old settings to fail.
|
||||
* 2.5.14 - 6 Feb 2023 - Simplify the UI for merging models, and some other minor UI tweaks. Better error reporting if a model failed to load.
|
||||
* 2.5.14 - 3 Feb 2023 - Fix the 'Make Similar Images' button, which was producing incorrect images (weren't very similar).
|
||||
* 2.5.13 - 1 Feb 2023 - Fix the remaining GPU memory leaks, including a better fix (more comprehensive) for the change in 2.5.12 (27 Jan).
|
||||
* 2.5.12 - 27 Jan 2023 - Fix a memory leak, which made the UI unresponsive after an out-of-memory error. The allocated memory is now freed-up after an error.
|
||||
* 2.5.11 - 25 Jan 2023 - UI for Merging Models. Thanks @JeLuf. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Model-Merging
|
||||
* 2.5.10 - 24 Jan 2023 - Reduce the VRAM usage for img2img in 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of this UI.
|
||||
* 2.5.9 - 23 Jan 2023 - Fix a bug where img2img would produce poorer-quality images for the same settings, as compared to version 2.4 of this UI.
|
||||
* 2.5.9 - 23 Jan 2023 - Reduce the VRAM usage for 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of the UI.
|
||||
* 2.5.8 - 17 Jan 2023 - Fix a bug where 'Low' VRAM usage would consume a LOT of VRAM (on higher-end GPUs). Also fixed a bug that caused out-of-memory errors on SD 2.1-768 models, on 'high' VRAM usage setting.
|
||||
* 2.5.7 - 16 Jan 2023 - Fix a bug where VAE files ending with .vae.pt weren't getting displayed. Thanks Madrang, rbertus2000 and JeLuf.
|
||||
* 2.5.6 - 10 Jan 2023 - `Fill` tool for the Image Editor, to allow filling areas with color (or the entire image). And some bug fixes to the Image Editor. Thanks @mdiller.
|
||||
* 2.5.6 - 10 Jan 2023 - Find Stable Diffusion models in sub-folders inside `models/stable-diffusion`. This allows you to organize your models into sub-folders, instead of keeping them all in a single folder. Thanks @JeLuf.
|
||||
* 2.5.5 - 9 Jan 2023 - Lots of bug fixes. Thanks @patriceac and @JeLuf.
|
||||
* 2.5.4 - 29 Dec 2022 - Press Esc key on the keyboard to close the Image Editor. Thanks @patriceac.
|
||||
* 2.5.4 - 29 Dec 2022 - Lots of bug fixes in the UI. Thanks @patriceac.
|
||||
* 2.5.4 - 28 Dec 2022 - Full support for running tasks in parallel on multiple GPUs. Warning: 'Euler Ancestral', 'DPM2 Ancestral' and 'DPM++ 2s Ancestral' may produce slight variations in the image (if run in parallel), so we recommend using the other samplers.
|
||||
* 2.5.3 - 27 Dec 2022 - Fix broken drag-and-drop for text metadata files (as well as paste in clipboard).
|
||||
* 2.5.3 - 27 Dec 2022 - Allow upscaling by 2x as well as 4x.
|
||||
* 2.5.3 - 27 Dec 2022 - Fix broken renders on a second GPU.
|
||||
* 2.5.3 - 26 Dec 2022 - Add a `Remove` button on each image. Thanks @JeLuf.
|
||||
* 2.5.2 - 26 Dec 2022 - Fix broken inpainting if using non-square target images.
|
||||
* 2.5.2 - 26 Dec 2022 - Fix a bug where an incorrect model config would get used for some SD 2.1 models.
|
||||
* 2.5.2 - 26 Dec 2022 - Slight performance and memory improvement while rendering using SD 2.1 models.
|
||||
* 2.5.1 - 25 Dec 2022 - Allow custom config yaml files for models. You can put a config file (`.yaml`) next to the model file, with the same name as the model. For e.g. if you put `robo-diffusion-v2-base.yaml` next to `robo-diffusion-v2-base.ckpt`, it'll automatically use that config file.
|
||||
* 2.5.1 - 25 Dec 2022 - Fix broken rendering for SD 2.1-768 models. Fix broken rendering SD 2.0 safetensor models.
|
||||
* 2.5.0 - 25 Dec 2022 - Major new release! Nearly twice as fast, Full support for SD 2.1 (including low GPU RAM optimizations), 6 new samplers, Model Merging, Fast loading/unloading of VAEs, Database of known models, Color correction for img2img, Three GPU Memory Usage Settings, Save metadata as JSON, Major rewrite of the code, Name change.
|
||||
|
||||
## v2.4
|
||||
### Major Changes
|
||||
- **Allow reordering the task queue** (by dragging and dropping tasks). Thanks @madrang
|
||||
- **Automatic scanning for malicious model files** - using `picklescan`, and support for `safetensor` model format. Thanks @JeLuf
|
||||
- **Image Editor** - for drawing simple images for guiding the AI. Thanks @mdiller
|
||||
- **Use pre-trained hypernetworks** - for improving the quality of images. Thanks @C0bra5
|
||||
- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder
|
||||
- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs
|
||||
- **Cleaner UI design** - Show settings and help in new tabs, instead of dropdown popups (which were buggy). Thanks @mdiller
|
||||
- **Progress bar.** Thanks @mdiller
|
||||
- **Custom Image Modifiers** - You can now save your custom image modifiers! Your saved modifiers can include special characters like `{}, (), [], |`
|
||||
- Drag and Drop **text files generated from previously saved images**, and copy settings to clipboard. Thanks @madrang
|
||||
- Paste settings from clipboard. Thanks @JeLuf
|
||||
- Bug fixes to reduce the chances of tasks crashing during long multi-hour runs (chrome can put long-running background tabs to sleep). Thanks @JeLuf and @madrang
|
||||
- **Improved documentation.** Thanks @JeLuf and @jsuelwald
|
||||
- Improved the codebase for dealing with system settings and UI settings. Thanks @mdiller
|
||||
- Help instructions next to some setttings, and in the tab
|
||||
- Show system info in the settings tab
|
||||
- Keyboard shortcut: Ctrl+Enter to start a task
|
||||
- Configuration to prevent the browser from opening on startup
|
||||
- Lots of minor bug fixes
|
||||
- A `What's New?` tab in the UI
|
||||
- Ask for a confimation before clearing the results pane or stopping a render task. The dialog can be skipped by holding down the shift key while clicking on the button.
|
||||
- Show the network addresses of the server in the systems setting dialog
|
||||
- Support loading models in the safetensor format, for improved safety
|
||||
|
||||
### Detailed changelog
|
||||
* 2.4.24 - 9 Jan 2022 - Urgent fix for failures on old/long-term-support browsers. Thanks @JeLuf.
|
||||
* 2.4.23/22 - 29 Dec 2022 - Allow rolling back from the upcoming v2.5 change (in beta).
|
||||
* 2.4.21 - 23 Dec 2022 - Speed up image creation, by removing a delay (regression) of 4-5 seconds between clicking the `Make Image` button and calling the server.
|
||||
* 2.4.20 - 22 Dec 2022 - `Pause All` button to pause all the pending tasks. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - `Undo`/`Redo` buttons in the image editor. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - Drag handle to reorder the tasks. This fixed a bug where the metadata was no longer selectable (for copying). Thanks @JeLuf
|
||||
* 2.4.19 - 17 Dec 2022 - Add Undo/Redo buttons in the Image Editor. Thanks @JeLuf
|
||||
* 2.4.19 - 10 Dec 2022 - Show init img in task list
|
||||
* 2.4.19 - 7 Dec 2022 - Use pre-trained hypernetworks while generating images. Thanks @C0bra5
|
||||
* 2.4.19 - 6 Dec 2022 - Allow processing new tasks first. Thanks @madrang
|
||||
* 2.4.19 - 6 Dec 2022 - Allow reordering the task queue (by dragging tasks). Thanks @madrang
|
||||
* 2.4.19 - 6 Dec 2022 - Re-organize the code, to make it easier to write user plugins. Thanks @madrang
|
||||
* 2.4.18 - 5 Dec 2022 - Make JPEG Output quality user controllable. Thanks @JeLuf
|
||||
* 2.4.18 - 5 Dec 2022 - Support loading models in the safetensor format, for improved safety. Thanks @JeLuf
|
||||
* 2.4.18 - 1 Dec 2022 - Image Editor, for drawing simple images for guiding the AI. Thanks @mdiller
|
||||
* 2.4.18 - 1 Dec 2022 - Disable an image modifier temporarily by right-clicking it. Thanks @patriceac
|
||||
* 2.4.17 - 30 Nov 2022 - Scroll to generated image. Thanks @patriceac
|
||||
* 2.4.17 - 30 Nov 2022 - Show the network addresses of the server in the systems setting dialog. Thanks @JeLuf
|
||||
* 2.4.17 - 30 Nov 2022 - Fix a bug where GFPGAN wouldn't work properly when multiple GPUs tried to run it at the same time. Thanks @madrang
|
||||
* 2.4.17 - 30 Nov 2022 - Confirm before stopping or clearing all the tasks. Thanks @JeLuf
|
||||
* 2.4.16 - 29 Nov 2022 - Bug fixes for SD 2.0 - remove the need for patching, default to SD 1.4 model if trying to load an SD2 model in SD1.4.
|
||||
* 2.4.15 - 25 Nov 2022 - Experimental support for SD 2.0. Uses lots of memory, not optimized, probably GPU-only.
|
||||
* 2.4.14 - 22 Nov 2022 - Change the backend to a custom fork of Stable Diffusion
|
||||
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
|
||||
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
||||
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
||||
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
||||
* 2.4.11 - 19 Nov 2022 - New system settings to manage the network config (port number and whether to only listen on localhost)
|
||||
* 2.4.11 - 19 Nov 2022 - Address a regression in how long images take to generate. Use the previous code for moving a model to CPU. This improves things by a second or two per image, but we still have a regression (investigating).
|
||||
* 2.4.10 - 18 Nov 2022 - Textarea for negative prompts. Thanks @JeLuf
|
||||
* 2.4.10 - 18 Nov 2022 - Improved design for Settings, and rounded toggle buttons instead of checkboxes for a more modern look. Thanks @mdiller
|
||||
* 2.4.9 - 18 Nov 2022 - Add Picklescan - a scanner for malicious model files. If it finds a malicious file, it will halt the web application and alert the user. Thanks @JeLuf
|
||||
* 2.4.8 - 18 Nov 2022 - A `Use these settings` button to use the settings from a previously generated image task. Thanks @patriceac
|
||||
* 2.4.7 - 18 Nov 2022 - Don't crash if a VAE file fails to load
|
||||
* 2.4.7 - 17 Nov 2022 - Fix a bug where Face Correction (GFPGAN) would fail on cuda:N (i.e. GPUs other than cuda:0), as well as fail on CPU if the system had an incompatible GPU.
|
||||
* 2.4.6 - 16 Nov 2022 - Fix a regression in VRAM usage during startup, which caused 'Out of Memory' errors when starting on GPUs with 4gb (or less) VRAM
|
||||
* 2.4.5 - 16 Nov 2022 - Add checkbox for "Open browser on startup".
|
||||
* 2.4.5 - 16 Nov 2022 - Add a directory for core plugins that ship with Stable Diffusion UI by default.
|
||||
* 2.4.5 - 16 Nov 2022 - Add a "What's New?" tab as a core plugin, which fetches the contents of CHANGES.md from the app's release branch.
|
51
CONTRIBUTING.md
Normal file
@ -0,0 +1,51 @@
|
||||
Hi there, these instructions are meant for the developers of this project.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
Thanks
|
||||
|
||||
# For developers:
|
||||
|
||||
If you would like to contribute to this project, there is a discord for discussion:
|
||||
[](https://discord.com/invite/u9yhsFmEkB)
|
||||
|
||||
## Development environment for UI (frontend and server) changes
|
||||
This is in-flux, but one way to get a development environment running for editing the UI of this project is:
|
||||
(swap `.sh` or `.bat` in instructions depending on your environment, and be sure to adjust any paths to match where you're working)
|
||||
|
||||
1) Install the project to a new location using the [usual installation process](https://github.com/cmdr2/stable-diffusion-ui#installation), e.g. to `/projects/stable-diffusion-ui-archive`
|
||||
2) Start the newly installed project, and check that you can view and generate images on `localhost:9000`
|
||||
3) Next, please clone the project repository using `git clone` (e.g. to `/projects/stable-diffusion-ui-repo`)
|
||||
4) Close the server (started in step 2), and edit `/projects/stable-diffusion-ui-archive/scripts/on_env_start.sh` (or `on_env_start.bat`)
|
||||
5) Comment out the lines near the bottom that copies the `files/ui` folder, e.g:
|
||||
|
||||
for `.sh`
|
||||
```
|
||||
# rm -rf ui
|
||||
# cp -Rf sd-ui-files/ui .
|
||||
# cp sd-ui-files/scripts/on_sd_start.sh scripts/
|
||||
# cp sd-ui-files/scripts/start.sh .
|
||||
```
|
||||
for `.bat`
|
||||
```
|
||||
REM @xcopy sd-ui-files\ui ui /s /i /Y
|
||||
REM @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
REM @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
```
|
||||
6) Next, comment out the line at the top of `/projects/stable-diffusion-ui-archive/scripts/on_sd_start.sh` (or `on_sd_start.bat`) that copies `on_env_start`. For e.g. `@rem @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y`
|
||||
8) Delete the current `ui` folder at `/projects/stable-diffusion-ui-archive/ui`
|
||||
9) Now make a symlink between the repository clone (where you will be making changes) and this archive (where you will be running stable diffusion):
|
||||
`ln -s /projects/stable-diffusion-ui-repo/ui /projects/stable-diffusion-ui-archive/ui`
|
||||
or for Windows
|
||||
`mklink /J \projects\stable-diffusion-ui-archive\ui \projects\stable-diffusion-ui-repo\ui` (link name first, source repo dir second)
|
||||
9) Run the project again (like in step 2) and ensure you can still use the UI.
|
||||
10) Congrats, now any changes you make in your repo `ui` folder are linked to this running archive of the app and can be previewed in the browser.
|
||||
11) Please update CHANGES.md in your pull requests.
|
||||
|
||||
Check the `ui/frontend/build/README.md` for instructions on running and building the React code.
|
||||
|
||||
## Development environment for Installer changes
|
||||
Build the Windows installer using Windows, and the Linux installer using Linux. Don't mix the two, and don't use WSL. An Ubuntu VM is fine for building the Linux installer on a Windows host.
|
||||
|
||||
1. Run `build.bat` or `./build.sh` depending on whether you're in Windows or Linux.
|
||||
2. Make a new GitHub release and upload the Windows and Linux installer builds created inside the `dist` folder.
|
15
Dockerfile
@ -1,15 +0,0 @@
|
||||
FROM python:3.9
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt update
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "9000"]
|
24
How to install and run.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Congrats on downloading Stable Diffusion UI, version 2!
|
||||
|
||||
If you haven't downloaded Stable Diffusion UI yet, please download from https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
After downloading, to install please follow these instructions:
|
||||
|
||||
For Windows:
|
||||
- Please double-click the "Start Stable Diffusion UI.cmd" file inside the "stable-diffusion-ui" folder.
|
||||
|
||||
For Linux:
|
||||
- Please open a terminal, and go to the "stable-diffusion-ui" directory. Then run ./start.sh
|
||||
|
||||
That file will automatically install everything. After that it will start the Stable Diffusion interface in a web browser.
|
||||
|
||||
To start the UI in the future, please run the same command mentioned above.
|
||||
|
||||
|
||||
If you have any problems, please:
|
||||
1. Try the troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
||||
2. Or, seek help from the community at https://discord.com/invite/u9yhsFmEkB
|
||||
3. Or, file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
|
||||
|
||||
Thanks
|
||||
cmdr2 (and contributors to the project)
|
1
NSIS/README.md
Normal file
@ -0,0 +1 @@
|
||||
Scripts to be used with the Nullsoft Scriptable Installation System
|
BIN
NSIS/astro.bmp
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
NSIS/sd.ico
Normal file
After Width: | Height: | Size: 200 KiB |
265
NSIS/sdui.nsi
Normal file
@ -0,0 +1,265 @@
|
||||
; Script generated by the HM NIS Edit Script Wizard.
|
||||
|
||||
Target x86-unicode
|
||||
Unicode True
|
||||
!AddPluginDir /x86-unicode "."
|
||||
; HM NIS Edit Wizard helper defines
|
||||
!define PRODUCT_NAME "Stable Diffusion UI"
|
||||
!define PRODUCT_VERSION "Installer 2.35"
|
||||
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
|
||||
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
|
||||
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Cmdr2\App Paths\installer.exe"
|
||||
|
||||
; MUI 1.67 compatible ------
|
||||
!include "MUI.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "nsDialogs.nsh"
|
||||
|
||||
Var Dialog
|
||||
Var Label
|
||||
Var Button
|
||||
|
||||
Var InstDirLen
|
||||
Var LongPathsEnabled
|
||||
Var AccountType
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; This function returns the number of spaces in a string.
|
||||
; The string is passed on the stack (using Push $STRING)
|
||||
; The result is also returned on the stack and can be consumed with Pop $var
|
||||
; https://nsis.sourceforge.io/Check_for_spaces_in_a_directory_path
|
||||
Function CheckForSpaces
|
||||
Exch $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
StrCpy $R1 -1
|
||||
StrCpy $R3 $R0
|
||||
StrCpy $R0 0
|
||||
loop:
|
||||
StrCpy $R2 $R3 1 $R1
|
||||
IntOp $R1 $R1 - 1
|
||||
StrCmp $R2 "" done
|
||||
StrCmp $R2 " " 0 loop
|
||||
IntOp $R0 $R0 + 1
|
||||
Goto loop
|
||||
done:
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; The function DirectoryLeave is called after the user chose the installation directory.
|
||||
; If it calls "abort", the user is sent back to choose a different directory.
|
||||
Function DirectoryLeave
|
||||
; check whether the installation directory path is longer than 30 characters.
|
||||
; If yes, we suggest to the user to enable long filename support
|
||||
;----------------------------------------------------------------------------
|
||||
StrLen $InstDirLen "$INSTDIR"
|
||||
|
||||
; Check whether the registry key that allows for >260 characters in a path name is set
|
||||
ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled"
|
||||
|
||||
${If} $InstDirLen > 30
|
||||
${AndIf} $LongPathsEnabled == "0"
|
||||
; Check whether we're in the Admin group
|
||||
UserInfo::GetAccountType
|
||||
Pop $AccountType
|
||||
|
||||
${If} $AccountType == "Admin"
|
||||
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION 'The path name is too long. $\n$\nYou can either enable long file name support in Windows,$\nor you can go back and choose a different path.$\n$\nFor details see: shorturl.at/auBD1$\n$\nEnable long path name support in Windows?' IDYES`
|
||||
; Enable long path names
|
||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation path name too long. The installation path must not have more than 30 characters."
|
||||
abort
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
; Check for spaces in the installation directory path.
|
||||
; ----------------------------------------------------
|
||||
|
||||
; $R0 = CheckForSpaces( $INSTDIR )
|
||||
Push $INSTDIR # Input string (install path).
|
||||
Call CheckForSpaces
|
||||
Pop $R0 # The function returns the number of spaces found in the input string.
|
||||
|
||||
; Check if any spaces exist in $INSTDIR.
|
||||
${If} $R0 != 0
|
||||
; Plural if more than 1 space in $INSTDIR.
|
||||
; If $R0 == 1: $R1 = ""; else: $R1 = "s"
|
||||
StrCmp $R0 1 0 +3
|
||||
StrCpy $R1 ""
|
||||
Goto +2
|
||||
StrCpy $R1 "s"
|
||||
|
||||
; Show message box then take the user back to the Directory page.
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Error: The Installaton directory \
|
||||
has $R0 space character$R1.$\nPlease choose an installation directory without space characters."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Check for NTFS filesystem. Installations on FAT fail.
|
||||
; -----------------------------------------------------
|
||||
StrCpy $5 $INSTDIR 3
|
||||
System::Call 'Kernel32::GetVolumeInformation(t "$5",t,i ${NSIS_MAX_STRLEN},*i,*i,*i,t.r1,i ${NSIS_MAX_STRLEN})i.r0'
|
||||
${If} $0 <> 0
|
||||
${AndIf} $1 == "NTFS"
|
||||
MessageBox mb_ok "$5 has filesystem type '$1'.$\nOnly NTFS filesystems are supported.$\nPlease choose a different drive."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Open the MS download page in a browser and enable the [Next] button
|
||||
Function MSMediaFeaturepack
|
||||
ExecShell "open" "https://www.microsoft.com/en-us/software-download/mediafeaturepack"
|
||||
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 1
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Install the MS Media Feature Pack, if it is missing (e.g. on Windows 10 N)
|
||||
Function MediaPackDialog
|
||||
!insertmacro MUI_HEADER_TEXT "Windows Media Feature Pack" "Required software module is missing"
|
||||
|
||||
; Skip this dialog if mf.dll is installed
|
||||
${If} ${FileExists} "$WINDIR\system32\mf.dll"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $Dialog
|
||||
|
||||
${If} $Dialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
${NSD_CreateLabel} 0 0 100% 48u "The Windows Media Feature Pack is missing on this computer. It is required for the Stable Diffusion UI.$\nYou can continue the installation after installing the Windows Media Feature Pack."
|
||||
Pop $Label
|
||||
|
||||
${NSD_CreateButton} 10% 49u 80% 12u "Download Meda Feature Pack from Microsoft"
|
||||
Pop $Button
|
||||
|
||||
GetFunctionAddress $0 MSMediaFeaturePack
|
||||
nsDialogs::OnClick $Button $0
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 0
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI Settings
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "sd.ico"
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "astro.bmp"
|
||||
|
||||
; Welcome page
|
||||
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Stable Diffusion UI.$\n$\n\
|
||||
Click Next to continue."
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
Page custom MediaPackDialog
|
||||
|
||||
; License page
|
||||
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
|
||||
; Directory page
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
; Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Finish page
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI end
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "Install Stable Diffusion UI.exe"
|
||||
InstallDir "C:\Stable-Diffusion-UI\"
|
||||
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
|
||||
ShowInstDetails show
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; List of files to be installed
|
||||
Section "MainSection" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
File "..\CreativeML Open RAIL-M License"
|
||||
File "..\How to install and run.txt"
|
||||
File "..\LICENSE"
|
||||
File "..\Start Stable Diffusion UI.cmd"
|
||||
SetOutPath "$INSTDIR\scripts"
|
||||
File "..\scripts\bootstrap.bat"
|
||||
File "..\scripts\install_status.txt"
|
||||
File "..\scripts\on_env_start.bat"
|
||||
File "C:\windows\system32\curl.exe"
|
||||
CreateDirectory "$INSTDIR\profile"
|
||||
CreateDirectory "$SMPROGRAMS\Stable Diffusion UI"
|
||||
CreateShortCut "$SMPROGRAMS\Stable Diffusion UI\Start Stable Diffusion UI.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
SectionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Our installer only needs 25 KB, but once it has run, we need 25 GB
|
||||
; So we need to overwrite the automatically detected space requirements.
|
||||
; https://nsis.sourceforge.io/Docs/Chapter4.html#4.9.13.7
|
||||
; The example in section 4.9.13.7 seems to be wrong: the number
|
||||
; needs to be provided in Kilobytes.
|
||||
Function .onInit
|
||||
; Set required size of section 'SEC01' to 25 Gigabytes
|
||||
SectionSetSize ${SEC01} 26214400
|
||||
|
||||
|
||||
; Check system meory size. We need at least 8GB
|
||||
; ----------------------------------------------------
|
||||
|
||||
; allocate a few bytes of memory
|
||||
System::Alloc 64
|
||||
Pop $1
|
||||
|
||||
; Retrieve HW info from the Windows Kernel
|
||||
System::Call "*$1(i64)"
|
||||
System::Call "Kernel32::GlobalMemoryStatusEx(i r1)"
|
||||
; unpack the data into $R2 - $R10
|
||||
System::Call "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
|
||||
|
||||
# free up the memory
|
||||
System::Free $1
|
||||
|
||||
; Result mapping:
|
||||
; "Structure size: $2 bytes"
|
||||
; "Memory load: $3%"
|
||||
; "Total physical memory: $4 bytes"
|
||||
; "Free physical memory: $5 bytes"
|
||||
; "Total page file: $6 bytes"
|
||||
; "Free page file: $7 bytes"
|
||||
; "Total virtual: $8 bytes"
|
||||
; "Free virtual: $9 bytes"
|
||||
|
||||
; Mem size in MB
|
||||
System::Int64Op $4 / 1048576
|
||||
Pop $4
|
||||
|
||||
${If} $4 < "8000"
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Warning!$\n$\nYour system has less than 8GB of memory (RAM).$\n$\n\
|
||||
You can still try to install Stable Diffusion UI,$\nbut it might have problems to start, or run$\nvery slowly."
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;Section -Post
|
||||
; WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\installer.exe"
|
||||
;SectionEnd
|
@ -1,15 +0,0 @@
|
||||
FROM python:3.9
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt update
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "old_port_main:app", "--host", "0.0.0.0", "--port", "8000"]
|
8
README BEFORE YOU RUN THIS.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Hi there,
|
||||
|
||||
What you have downloaded is meant for the developers of this project, not for users.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.
|
||||
Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
Thanks
|
202
README.md
@ -1,96 +1,146 @@
|
||||
# Stable Diffusion UI
|
||||
### A simple way to install and use [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion) on your own computer
|
||||
# Easy Diffusion 2.5
|
||||
### The easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer.
|
||||
|
||||
---
|
||||
Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community.
|
||||
|
||||
🎉 **New!** `img2img` and `inpaint` (masking) are now supported! You can provide an image to generate new images based on it (and an optional text prompt). You can also use the generated image as the new input image in 1-click, to refine it further. (Thanks [Andreas](https://github.com/andreasjansson)!)
|
||||
[Installation guide](#step-1-download-and-extract-the-installer) | [Troubleshooting guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) | <sub>[](https://discord.com/invite/u9yhsFmEkB)</sub> <sup>(for support queries, and development discussions)</sup>
|
||||
|
||||
# What does this do?
|
||||
Two things:
|
||||
1. Automatically downloads and installs Stable Diffusion on your own computer (no need to mess with conda or environments)
|
||||
2. Gives you a simple browser-based UI to talk to your local Stable Diffusion. Enter text prompts and view the generated image. No API keys required.
|
||||

|
||||
|
||||
All the processing will happen on your computer locally, it does not transmit your prompts or process on any remote server.
|
||||
# Step 1: Download and extract the installer
|
||||
Click the download button for your operating system:
|
||||
|
||||
<p float="left">
|
||||
<img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/shot-v3a.jpg" height="500" />
|
||||
<img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/shot-v6a.jpg" height="500" />
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.5.15/stable-diffusion-ui-windows.zip"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-win.png" width="200" /></a>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.5.15/stable-diffusion-ui-linux.zip"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-linux.png" width="200" /></a>
|
||||
</p>
|
||||
|
||||
## On Windows:
|
||||
1. Unzip/extract the folder `stable-diffusion-ui` which should be in your downloads folder, unless you changed your default downloads destination.
|
||||
2. Move the `stable-diffusion-ui` folder to your `C:` drive (or any other drive like `D:`, at the top root level). `C:\stable-diffusion-ui` or `D:\stable-diffusion-ui` as examples. This will avoid a common problem with Windows (file path length limits).
|
||||
## On Linux:
|
||||
1. Unzip/extract the folder `stable-diffusion-ui` which should be in your downloads folder, unless you changed your default downloads destination.
|
||||
2. Open a terminal window, and navigate to the `stable-diffusion-ui` directory.
|
||||
|
||||
# Step 2: Run the program
|
||||
## On Windows:
|
||||
Double-click `Start Stable Diffusion UI.cmd`.
|
||||
If Windows SmartScreen prevents you from running the program click `More info` and then `Run anyway`.
|
||||
## On Linux:
|
||||
Run `./start.sh` (or `bash start.sh`) in a terminal.
|
||||
|
||||
The installer will take care of whatever is needed. If you face any problems, you can join the friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) and ask for assistance.
|
||||
|
||||
# Step 3: There is no Step 3. It's that simple!
|
||||
|
||||
**To Uninstall:** Just delete the `stable-diffusion-ui` folder to uninstall all the downloaded packages.
|
||||
|
||||
----
|
||||
|
||||
# Easy for new users, powerful features for advanced users
|
||||
## Features:
|
||||
|
||||
### User experience
|
||||
- **Hassle-free installation**: Does not require technical knowledge, does not require pre-installed software. Just download and run!
|
||||
- **Clutter-free UI**: A friendly and simple UI, while providing a lot of powerful features.
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish.
|
||||
- **Intelligent Model Detection**: Automatically figures out the YAML config file to use for the chosen model (via a models database).
|
||||
- **Live Preview**: See the image as the AI is drawing it.
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **Multiple Prompts File**: Queue multiple prompts by entering one prompt per line, or by running a text file.
|
||||
- **Save generated images to disk**: Save your images to your PC!
|
||||
- **UI Themes**: Customize the program to your liking.
|
||||
- **Searchable models dropdown**: organize your models into sub-folders, and search through them in the UI.
|
||||
|
||||
### Image generation
|
||||
- **Supports**: "*Text to Image*" and "*Image to Image*".
|
||||
- **19 Samplers**: `ddim`, `plms`, `heun`, `euler`, `euler_a`, `dpm2`, `dpm2_a`, `lms`, `dpm_solver_stability`, `dpmpp_2s_a`, `dpmpp_2m`, `dpmpp_sde`, `dpm_fast`, `dpm_adaptive`, `unipc_snr`, `unipc_tu`, `unipc_tq`, `unipc_snr_2`, `unipc_tu_2`.
|
||||
- **In-Painting**: Specify areas of your image to paint into.
|
||||
- **Simple Drawing Tool**: Draw basic images to guide the AI, without needing an external drawing program.
|
||||
- **Face Correction (GFPGAN)**
|
||||
- **Upscaling (RealESRGAN)**
|
||||
- **Loopback**: Use the output image as the input image for the next img2img task.
|
||||
- **Negative Prompt**: Specify aspects of the image to *remove*.
|
||||
- **Attention/Emphasis**: () in the prompt increases the model's attention to enclosed words, and [] decreases it.
|
||||
- **Weighted Prompts**: Use weights for specific words in your prompt to change their importance, e.g. `red:2.4 dragon:1.2`.
|
||||
- **Prompt Matrix**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`.
|
||||
- **1-click Upscale/Face Correction**: Upscale or correct an image after it has been generated.
|
||||
- **Make Similar Images**: Click to generate multiple variations of a generated image.
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*.
|
||||
- **JPEG/PNG/WEBP output**: Multiple file formats.
|
||||
|
||||
### Advanced features
|
||||
- **Custom Models**: Use your own `.ckpt` or `.safetensors` file, by placing it inside the `models/stable-diffusion` folder!
|
||||
- **Stable Diffusion 2.1 support**
|
||||
- **Merge Models**
|
||||
- **Use custom VAE models**
|
||||
- **Use pre-trained Hypernetworks**
|
||||
- **Use custom GFPGAN models**
|
||||
- **UI Plugins**: Choose from a growing list of [community-generated UI plugins](https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Plugins), or write your own plugin to add features to the project!
|
||||
|
||||
### Performance and security
|
||||
- **Fast**: Creates a 512x512 image with euler_a in 5 seconds, on an NVIDIA 3060 12GB.
|
||||
- **Low Memory Usage**: Create 512x512 images with less than 3 GB of GPU RAM, and 768x768 images with less than 4 GB of GPU RAM!
|
||||
- **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU.
|
||||
- **Multi-GPU support**: Automatically spreads your tasks across multiple GPUs (if available), for faster performance!
|
||||
- **Auto scan for malicious models**: Uses picklescan to prevent malicious models.
|
||||
- **Safetensors support**: Support loading models in the safetensor format, for improved safety.
|
||||
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
|
||||
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, and edit the conda environment.
|
||||
|
||||
**(and a lot more)**
|
||||
|
||||
----
|
||||
|
||||
## Easy for new users:
|
||||

|
||||
|
||||
|
||||
## Powerful features for advanced users:
|
||||

|
||||
|
||||
|
||||
## Live Preview
|
||||
Useful for judging (and stopping) an image quickly, without waiting for it to finish rendering.
|
||||
|
||||

|
||||
|
||||
## Task Queue
|
||||

|
||||
|
||||
|
||||
|
||||
# System Requirements
|
||||
1. Computer capable of running Stable Diffusion.
|
||||
2. Linux or Windows 11 (with [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)) or Windows 10 v2004+ (Build 19041+) with [WSL](https://docs.microsoft.com/en-us/windows/wsl/install).
|
||||
3. Requires (a) [Docker](https://docs.docker.com/engine/install/), (b) [docker-compose v1.29](https://docs.docker.com/compose/install/), and (c) [nvidia-container-toolkit](https://stackoverflow.com/a/58432877).
|
||||
1. Windows 10/11, or Linux. Experimental support for Mac is coming soon.
|
||||
2. An NVIDIA graphics card, preferably with 4GB or more of VRAM. If you don't have a compatible graphics card, it'll automatically run in the slower "CPU Mode".
|
||||
3. Minimum 8 GB of RAM and 25GB of disk space.
|
||||
|
||||
**Important:** If you're using Windows, please install docker inside your [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)'s Linux. Install docker for the Linux distro in your WSL. **Don't install Docker for Windows.**
|
||||
You don't need to install or struggle with Python, Anaconda, Docker etc. The installer will take care of whatever is needed.
|
||||
|
||||
# Installation
|
||||
1. Clone this repository: `git clone https://github.com/cmdr2/stable-diffusion-ui.git` or [download the zip file](https://github.com/cmdr2/stable-diffusion-ui/archive/refs/heads/main.zip) and unzip.
|
||||
2. Open your terminal, and in the project directory run: `./server` (warning: this will take some time during the first run, since it'll download Stable Diffusion's [docker image](https://replicate.com/stability-ai/stable-diffusion), nearly 17 GiB)
|
||||
3. Open http://localhost:9000 in your browser. That's it!
|
||||
----
|
||||
|
||||
If you're getting errors, please check the [Troubleshooting](#troubleshooting) section below.
|
||||
|
||||
To stop the server, please run `./server stop`
|
||||
|
||||
# Usage
|
||||
Open http://localhost:9000 in your browser (after running `./server` from step 2 previously).
|
||||
|
||||
## With a text description
|
||||
1. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||
2. Press `Make Image`. This will take some time, depending on your system's processing power.
|
||||
3. See the image generated using your prompt.
|
||||
|
||||
## With an image
|
||||
1. Click `Browse..` next to `Initial Image`. Select your desired image.
|
||||
2. An optional text prompt can help you further describe the kind of image you want to generate.
|
||||
3. Press `Make Image`. See the image generated using your prompt.
|
||||
|
||||
You can also set an `Image Mask` for telling Stable Diffusion to draw in only the black areas in your image mask. White areas in your mask will be ignored.
|
||||
|
||||
**Pro tip:** You can also click `Use as Input` on a generated image, to use it as the input image for your next generation. This can be useful for sequentially refining the generated image with a single click.
|
||||
|
||||
**Another tip:** Images with the same aspect ratio of your generated image work best. E.g. 1:1 if you're generating images sized 512x512.
|
||||
|
||||
## Problems?
|
||||
Please [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues) if this did not work for you (after trying the common [troubleshooting](#troubleshooting) steps)!
|
||||
|
||||
# Advanced Settings
|
||||
You can also set the configuration like `seed`, `width`, `height`, `num_outputs`, `num_inference_steps` and `guidance_scale` using the 'show' button next to 'Advanced settings'.
|
||||
|
||||
Use the same `seed` number to get the same image for a certain prompt. This is useful for refining a prompt without losing the basic image design. Enable the `random images` checkbox to get random images.
|
||||
|
||||

|
||||
|
||||
# Troubleshooting
|
||||
## './docker-compose.yml' is invalid:
|
||||
> ERROR: The Compose file './docker-compose.yml' is invalid because:
|
||||
> services.stability-ai.deploy.resources.reservations value Additional properties are not allowed ('devices' was unexpected)
|
||||
|
||||
Please ensure you have `docker-compose` version 1.29 or higher. Check `docker-compose --version`, and if required [update it to 1.29](https://docs.docker.com/compose/install/). (Thanks [HVRyan](https://github.com/HVRyan))
|
||||
|
||||
## RuntimeError: Found no NVIDIA driver on your system:
|
||||
If you have an NVIDIA GPU and the latest [NVIDIA driver](http://www.nvidia.com/Download/index.aspx), please ensure that you've installed [nvidia-container-toolkit](https://stackoverflow.com/a/58432877). (Thanks [u/exintrovert420](https://www.reddit.com/user/exintrovert420/))
|
||||
|
||||
## Some other process is already running at port 9000 / port 9000 could not be bound
|
||||
You can override the port used. Please change `docker-compose.yml` inside the project directory, and update the line `9000:9000` to `1337:9000` (where 1337 is whichever port number you want).
|
||||
|
||||
After doing this, please restart your server, by running `./server restart`.
|
||||
|
||||
After this, you can access the server at `http://localhost:1337` (where 1337 is the new port you specified earlier).
|
||||
|
||||
# Behind the scenes
|
||||
This project is a quick way to get started with Stable Diffusion. You do not need to have Stable Diffusion already installed, and do not need any API keys. This project will automatically download Stable Diffusion's docker image, the first time it is run.
|
||||
|
||||
This project runs Stable Diffusion in a docker container behind the scenes, using Stable Diffusion's [Docker image](https://replicate.com/stability-ai/stable-diffusion) on replicate.com.
|
||||
# How to use?
|
||||
Please refer to our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use) to understand how to use the features in this UI.
|
||||
|
||||
# Bugs reports and code contributions welcome
|
||||
If there are any problems or suggestions, please feel free to [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
Also, please feel free to submit a pull request, if you have any code contributions in mind.
|
||||
We could really use help on these aspects (click to view tasks that need your help):
|
||||
* [User Interface](https://github.com/users/cmdr2/projects/1/views/1)
|
||||
* [Engine](https://github.com/users/cmdr2/projects/3/views/1)
|
||||
* [Installer](https://github.com/users/cmdr2/projects/4/views/1)
|
||||
* [Documentation](https://github.com/users/cmdr2/projects/5/views/1)
|
||||
|
||||
If you have any code contributions in mind, please feel free to say Hi to us on the [discord server](https://discord.com/invite/u9yhsFmEkB). We use the Discord server for development-related discussions, and for helping users.
|
||||
|
||||
# Disclaimer
|
||||
The authors of this project are not responsible for any content generated using this interface.
|
||||
|
||||
This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, spread misinformation and target vulnerable groups. For the full list of restrictions please read [the license](LICENSE).
|
||||
The license of this software forbids you from sharing any content that:
|
||||
- Violates any laws.
|
||||
- Produces any harm to a person or persons.
|
||||
- Disseminates (spreads) any personal information that would be meant for harm.
|
||||
- Spreads misinformation.
|
||||
- Target vulnerable groups.
|
||||
|
||||
For the full list of restrictions please read [the License](LICENSE). You agree to these terms by using this software.
|
||||
|
47
build.bat
Normal file
@ -0,0 +1,47 @@
|
||||
@echo off
|
||||
|
||||
@echo "Hi there, what you are running is meant for the developers of this project, not for users." & echo.
|
||||
@echo "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file."
|
||||
@echo "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation" & echo.
|
||||
@echo "If you are actually a developer of this project, please type Y and press enter" & echo.
|
||||
|
||||
set /p answer=Are you a developer of this project (Y/N)?
|
||||
if /i "%answer:~,1%" NEQ "Y" exit /b
|
||||
|
||||
mkdir dist\win\stable-diffusion-ui\scripts
|
||||
@REM mkdir dist\linux-mac\stable-diffusion-ui\scripts
|
||||
|
||||
@rem copy the installer files for Windows
|
||||
|
||||
copy scripts\on_env_start.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy scripts\bootstrap.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy "scripts\Start Stable Diffusion UI.cmd" dist\win\stable-diffusion-ui\
|
||||
copy LICENSE dist\win\stable-diffusion-ui\
|
||||
copy "CreativeML Open RAIL-M License" dist\win\stable-diffusion-ui\
|
||||
copy "How to install and run.txt" dist\win\stable-diffusion-ui\
|
||||
echo. > dist\win\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem copy the installer files for Linux and Mac
|
||||
|
||||
@REM copy scripts\on_env_start.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\bootstrap.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\start.sh dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy LICENSE dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "CreativeML Open RAIL-M License" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "How to install and run.txt" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM echo. > dist\linux-mac\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem make the zip
|
||||
|
||||
cd dist\win
|
||||
call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-windows.zip
|
||||
cd ..\..
|
||||
|
||||
@REM cd dist\linux-mac
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-linux.zip
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-mac.zip
|
||||
@REM cd ..\..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
||||
|
||||
pause
|
49
build.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
printf "Hi there, what you are running is meant for the developers of this project, not for users.\n\n"
|
||||
printf "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.\n"
|
||||
printf "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation\n\n"
|
||||
printf "If you are actually a developer of this project, please type Y and press enter\n\n"
|
||||
|
||||
read -p "Are you a developer of this project (Y/N) " yn
|
||||
case $yn in
|
||||
[Yy]* ) ;;
|
||||
* ) exit;;
|
||||
esac
|
||||
|
||||
# mkdir -p dist/win/stable-diffusion-ui/scripts
|
||||
mkdir -p dist/linux-mac/stable-diffusion-ui/scripts
|
||||
|
||||
# copy the installer files for Windows
|
||||
|
||||
# cp scripts/on_env_start.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp scripts/bootstrap.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp "scripts/Start Stable Diffusion UI.cmd" dist/win/stable-diffusion-ui/
|
||||
# cp LICENSE dist/win/stable-diffusion-ui/
|
||||
# cp "CreativeML Open RAIL-M License" dist/win/stable-diffusion-ui/
|
||||
# cp "How to install and run.txt" dist/win/stable-diffusion-ui/
|
||||
# echo "" > dist/win/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# copy the installer files for Linux and Mac
|
||||
|
||||
cp scripts/on_env_start.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/bootstrap.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/functions.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/start.sh dist/linux-mac/stable-diffusion-ui/
|
||||
cp LICENSE dist/linux-mac/stable-diffusion-ui/
|
||||
cp "CreativeML Open RAIL-M License" dist/linux-mac/stable-diffusion-ui/
|
||||
cp "How to install and run.txt" dist/linux-mac/stable-diffusion-ui/
|
||||
echo "" > dist/linux-mac/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# make the zip
|
||||
|
||||
# cd dist/win
|
||||
# zip -r ../stable-diffusion-ui-windows.zip stable-diffusion-ui
|
||||
# cd ../..
|
||||
|
||||
cd dist/linux-mac
|
||||
zip -r ../stable-diffusion-ui-linux.zip stable-diffusion-ui
|
||||
zip -r ../stable-diffusion-ui-mac.zip stable-diffusion-ui
|
||||
cd ../..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
@ -1,38 +0,0 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
stability-ai:
|
||||
container_name: sd
|
||||
ports:
|
||||
- '5000:5000'
|
||||
image: 'r8.im/stability-ai/stable-diffusion@sha256:3080f37ef32771c9984d65033cbe71caa96c69680008bae64cf691724a6df04c'
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
|
||||
stable-diffusion-ui:
|
||||
container_name: sd-ui
|
||||
ports:
|
||||
- '9000:9000'
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- .:/app
|
||||
depends_on:
|
||||
- stability-ai
|
||||
|
||||
stable-diffusion-old-port-redirect:
|
||||
container_name: sd-old-port-redirect
|
||||
ports:
|
||||
- '8000:8000'
|
||||
build:
|
||||
context: .
|
||||
dockerfile: OldPortDockerfile
|
||||
volumes:
|
||||
- .:/app
|
||||
|
||||
networks:
|
||||
default:
|
7
environment.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
name: stable-diffusion-ui-installer
|
||||
channels:
|
||||
- defaults
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- conda
|
||||
- git
|
471
index.html
@ -1,471 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
a {
|
||||
color: rgb(0, 102, 204);
|
||||
}
|
||||
a:visited {
|
||||
color: rgb(0, 102, 204);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
label {
|
||||
font-size: 10pt;
|
||||
}
|
||||
#prompt {
|
||||
width: 50vw;
|
||||
height: 50pt;
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
#prompt {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
.image_preview_container {
|
||||
display: none;
|
||||
}
|
||||
.image_clear_btn {
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
color: white;
|
||||
border: 2pt solid #ccc;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
border-radius: 8pt;
|
||||
width: 16pt;
|
||||
height: 16pt;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#configHeader {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#config {
|
||||
font-size: 9pt;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
#outputMsg {
|
||||
font-size: small;
|
||||
}
|
||||
#footer {
|
||||
border-top: 1px solid #999;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
font-size: small;
|
||||
}
|
||||
.imgUseBtn {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 5pt;
|
||||
margin-left: -5pt;
|
||||
}
|
||||
.imgItem {
|
||||
display: inline;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
<body>
|
||||
<div id="status">Server status: <span id="serverStatus">checking..</span> | Request status: <span id="reqStatus">n/a</span></div>
|
||||
|
||||
<br/>
|
||||
|
||||
<b>Prompt:</b><br/>
|
||||
<textarea id="prompt">a photograph of an astronaut riding a horse</textarea><br/>
|
||||
|
||||
<label for="init_image"><b>Initial Image:</b> (optional) </label> <input id="init_image" name="init_image" type="file" /> </button><br/>
|
||||
<div id="init_image_preview_container" class="image_preview_container">
|
||||
<img id="init_image_preview" src="" width="100" height="100" />
|
||||
<button id="init_image_clear" class="image_clear_btn">X</button>
|
||||
</div><br/>
|
||||
|
||||
<div id="mask_setting">
|
||||
<label for="mask"><b>Image Mask:</b> (optional) </label> <input id="mask" name="mask" type="file" /> </button><br/>
|
||||
<div id="mask_preview_container" class="image_preview_container">
|
||||
<img id="mask_preview" src="" width="100" height="100" />
|
||||
<button id="mask_clear" class="image_clear_btn">X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="configHeader"><b>Advanced settings:</b> [<a id="configToggleBtn" href="#">show</a>]</div>
|
||||
<div id="config">
|
||||
<label for="seed">Seed:</label> <input id="seed" name="seed" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked> <label for="random_seed">Random Image</label> <br/>
|
||||
<label for="num_outputs">Number of outputs:</label> <select id="num_outputs" name="num_outputs" value="1"><option value="1" selected>1</option><option value="4">4</option></select><br/>
|
||||
<label for="width">Width:</label> <select id="width" name="width" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option><option value="1024">1024</option></select><br/>
|
||||
<label for="height">Height:</label> <select id="height" name="height" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option></select><br/>
|
||||
<label for="num_inference_steps">Number of inference steps:</label> <input id="num_inference_steps" name="num_inference_steps" value="50"><br/>
|
||||
<label for="guidance_scale">Guidance Scale:</label> <input id="guidance_scale" name="guidance_scale" value="75" type="range" min="10" max="200"> <span id="guidance_scale_value"></span><br/>
|
||||
<span id="prompt_strength_container"><label for="prompt_strength">Prompt Strength:</label> <input id="prompt_strength" name="prompt_strength" value="8" type="range" min="0" max="10"> <span id="prompt_strength_value"></span><br/></span><br/>
|
||||
<input id="sound_toggle" name="sound_toggle" type="checkbox" checked> <label for="sound_toggle">Play sound on task completion</label><br/>
|
||||
</div>
|
||||
|
||||
<button id="makeImage">Make Image</button> <br/><br/>
|
||||
|
||||
<div id="outputMsg"></div>
|
||||
|
||||
<div id="images"></div>
|
||||
|
||||
<div id="footer">
|
||||
<p>Please feel free to <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const SOUND_ENABLED_KEY = "soundEnabled"
|
||||
const HEALTH_PING_INTERVAL = 5 // seconds
|
||||
|
||||
let promptField = document.querySelector('#prompt')
|
||||
let numOutputsField = document.querySelector('#num_outputs')
|
||||
let numInferenceStepsField = document.querySelector('#num_inference_steps')
|
||||
let guidanceScaleField = document.querySelector('#guidance_scale')
|
||||
let guidanceScaleValueLabel = document.querySelector('#guidance_scale_value')
|
||||
let randomSeedField = document.querySelector("#random_seed")
|
||||
let seedField = document.querySelector('#seed')
|
||||
let widthField = document.querySelector('#width')
|
||||
let heightField = document.querySelector('#height')
|
||||
let initImageSelector = document.querySelector("#init_image")
|
||||
let initImagePreview = document.querySelector("#init_image_preview")
|
||||
let maskImageSelector = document.querySelector("#mask")
|
||||
let maskImagePreview = document.querySelector("#mask_preview")
|
||||
let promptStrengthField = document.querySelector('#prompt_strength')
|
||||
let promptStrengthValueLabel = document.querySelector('#prompt_strength_value')
|
||||
|
||||
let makeImageBtn = document.querySelector('#makeImage')
|
||||
|
||||
let imagesContainer = document.querySelector('#images')
|
||||
let initImagePreviewContainer = document.querySelector('#init_image_preview_container')
|
||||
let initImageClearBtn = document.querySelector('#init_image_clear')
|
||||
let promptStrengthContainer = document.querySelector('#prompt_strength_container')
|
||||
|
||||
let maskSetting = document.querySelector('#mask_setting')
|
||||
let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
|
||||
let maskImageClearBtn = document.querySelector('#mask_clear')
|
||||
|
||||
let showConfigToggle = document.querySelector('#configToggleBtn')
|
||||
let configBox = document.querySelector('#config')
|
||||
let outputMsg = document.querySelector('#outputMsg')
|
||||
|
||||
let soundToggle = document.querySelector('#sound_toggle')
|
||||
|
||||
let serverStatus = 'offline'
|
||||
|
||||
function isSoundEnabled() {
|
||||
if (localStorage.getItem(SOUND_ENABLED_KEY) === 'false') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function setStatus(statusType, msg, msgType) {
|
||||
let el = ''
|
||||
|
||||
if (statusType === 'server') {
|
||||
el = '#serverStatus'
|
||||
serverStatus = msg
|
||||
} else if (statusType === 'request') {
|
||||
el = '#reqStatus'
|
||||
}
|
||||
|
||||
if (msgType == 'error') {
|
||||
msg = '<span style="color: red">' + msg + '<span>'
|
||||
} else if (msgType == 'success') {
|
||||
msg = '<span style="color: green">' + msg + '<span>'
|
||||
}
|
||||
|
||||
if (el) {
|
||||
document.querySelector(el).innerHTML = msg
|
||||
}
|
||||
}
|
||||
|
||||
function playSound() {
|
||||
const audio = new Audio('/media/ding.mp3')
|
||||
audio.volume = 0.2
|
||||
audio.play()
|
||||
}
|
||||
|
||||
async function healthCheck() {
|
||||
try {
|
||||
let res = await fetch('/ping')
|
||||
res = await res.json()
|
||||
|
||||
if (res[0] == 'OK') {
|
||||
setStatus('server', 'online', 'success')
|
||||
} else {
|
||||
setStatus('server', 'offline', 'error')
|
||||
}
|
||||
} catch (e) {
|
||||
setStatus('server', 'offline', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function makeImage() {
|
||||
setStatus('request', 'fetching..')
|
||||
|
||||
makeImageBtn.innerHTML = 'Processing..'
|
||||
makeImageBtn.disabled = true
|
||||
|
||||
outputMsg.innerHTML = 'Fetching..'
|
||||
|
||||
function logError(msg, res) {
|
||||
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
|
||||
console.log('request error', res)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000) : seedField.value)
|
||||
|
||||
let reqBody = {
|
||||
prompt: promptField.value,
|
||||
num_outputs: numOutputsField.value,
|
||||
num_inference_steps: numInferenceStepsField.value,
|
||||
guidance_scale: guidanceScaleField.value / 10,
|
||||
width: widthField.value,
|
||||
height: heightField.value,
|
||||
seed: seed,
|
||||
}
|
||||
|
||||
if (initImagePreview.src.indexOf('data:image/png;base64') !== -1) {
|
||||
reqBody['init_image'] = initImagePreview.src
|
||||
reqBody['prompt_strength'] = promptStrengthField.value / 10
|
||||
|
||||
if (maskImagePreview.src.indexOf('data:image/png;base64') !== -1) {
|
||||
reqBody['mask'] = maskImagePreview.src
|
||||
}
|
||||
}
|
||||
|
||||
let res = ''
|
||||
let time = new Date().getTime()
|
||||
|
||||
try {
|
||||
res = await fetch('/image', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqBody)
|
||||
})
|
||||
|
||||
if (res.status != 200) {
|
||||
if (serverStatus === 'online') {
|
||||
logError('Stable Diffusion had an error: ' + await res.text() + '. This happens sometimes. Maybe modify the prompt or seed a little bit?', res)
|
||||
} else {
|
||||
logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed.", res)
|
||||
}
|
||||
res = undefined
|
||||
} else {
|
||||
res = await res.json()
|
||||
|
||||
if (res.status !== 'succeeded') {
|
||||
let msg = ''
|
||||
if (res.detail !== undefined) {
|
||||
msg = res.detail[0].msg + " in " + JSON.stringify(res.detail[0].loc)
|
||||
} else {
|
||||
msg = res
|
||||
}
|
||||
logError(msg, res)
|
||||
res = undefined
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('request error', e)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
makeImageBtn.innerHTML = 'Make Image'
|
||||
makeImageBtn.disabled = false
|
||||
|
||||
if (isSoundEnabled()) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
|
||||
time = new Date().getTime() - time
|
||||
time /= 1000
|
||||
|
||||
outputMsg.innerHTML = 'Processed in ' + time + ' seconds. Seed: ' + seed
|
||||
|
||||
imagesContainer.innerHTML = ''
|
||||
|
||||
for (let idx in res.output) {
|
||||
let imgBody = ''
|
||||
|
||||
try {
|
||||
imgBody = res.output[idx]
|
||||
} catch (e) {
|
||||
console.log(imgBody)
|
||||
setStatus('request', 'invalid image', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
let imgItem = document.createElement('div')
|
||||
imgItem.className = 'imgItem'
|
||||
|
||||
let img = document.createElement('img')
|
||||
img.width = parseInt(reqBody.width)
|
||||
img.height = parseInt(reqBody.height)
|
||||
img.src = imgBody
|
||||
|
||||
let imgUseBtn = document.createElement('button')
|
||||
imgUseBtn.className = 'imgUseBtn'
|
||||
imgUseBtn.innerHTML = 'Use as Input'
|
||||
|
||||
imgItem.appendChild(img)
|
||||
imgItem.appendChild(imgUseBtn)
|
||||
imagesContainer.appendChild(imgItem)
|
||||
|
||||
imgUseBtn.addEventListener('click', function() {
|
||||
initImageSelector.value = null
|
||||
initImagePreview.src = imgBody
|
||||
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
|
||||
randomSeedField.checked = false
|
||||
seedField.value = seed
|
||||
seedField.disabled = false
|
||||
})
|
||||
}
|
||||
|
||||
setStatus('request', 'done', 'success')
|
||||
|
||||
if (randomSeedField.checked) {
|
||||
seedField.value = seed
|
||||
}
|
||||
}
|
||||
|
||||
function handleAudioEnabledChange(e) {
|
||||
localStorage.setItem(SOUND_ENABLED_KEY, e.target.checked.toString())
|
||||
}
|
||||
|
||||
soundToggle.addEventListener('click', handleAudioEnabledChange)
|
||||
soundToggle.checked = isSoundEnabled();
|
||||
|
||||
makeImageBtn.addEventListener('click', makeImage)
|
||||
|
||||
configBox.style.display = 'none'
|
||||
|
||||
showConfigToggle.addEventListener('click', function() {
|
||||
configBox.style.display = (configBox.style.display === 'none' ? 'block' : 'none')
|
||||
showConfigToggle.innerHTML = (configBox.style.display === 'none' ? 'show' : 'hide')
|
||||
return false
|
||||
})
|
||||
|
||||
function updateGuidanceScale() {
|
||||
guidanceScaleValueLabel.innerHTML = guidanceScaleField.value / 10
|
||||
}
|
||||
|
||||
guidanceScaleField.addEventListener('input', updateGuidanceScale)
|
||||
updateGuidanceScale()
|
||||
|
||||
function updatePromptStrength() {
|
||||
promptStrengthValueLabel.innerHTML = promptStrengthField.value / 10
|
||||
}
|
||||
|
||||
promptStrengthField.addEventListener('input', updatePromptStrength)
|
||||
updatePromptStrength()
|
||||
|
||||
function checkRandomSeed() {
|
||||
if (randomSeedField.checked) {
|
||||
seedField.disabled = true
|
||||
seedField.value = "random"
|
||||
} else {
|
||||
seedField.disabled = false
|
||||
}
|
||||
}
|
||||
randomSeedField.addEventListener('input', checkRandomSeed)
|
||||
checkRandomSeed()
|
||||
|
||||
function showInitImagePreview() {
|
||||
if (initImageSelector.files.length === 0) {
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
maskSetting.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
let file = initImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
// console.log(file.name, reader.result)
|
||||
initImagePreview.src = reader.result
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
initImageSelector.addEventListener('change', showInitImagePreview)
|
||||
showInitImagePreview()
|
||||
|
||||
initImageClearBtn.addEventListener('click', function() {
|
||||
initImageSelector.value = null
|
||||
maskImageSelector.value = null
|
||||
|
||||
initImagePreview.src = ''
|
||||
maskImagePreview.src = ''
|
||||
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
|
||||
maskSetting.style.display = 'none'
|
||||
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
})
|
||||
|
||||
function showMaskImagePreview() {
|
||||
if (maskImageSelector.files.length === 0) {
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
let file = maskImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
maskImagePreview.src = reader.result
|
||||
maskImagePreviewContainer.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
maskImageSelector.addEventListener('change', showMaskImagePreview)
|
||||
showMaskImagePreview()
|
||||
|
||||
maskImageClearBtn.addEventListener('click', function() {
|
||||
maskImageSelector.value = null
|
||||
maskImagePreview.src = ''
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
})
|
||||
|
||||
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||
</script>
|
||||
|
||||
</html>
|
69
main.py
@ -1,69 +0,0 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from starlette.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
import requests
|
||||
|
||||
LOCAL_SERVER_URL = 'http://stability-ai:5000'
|
||||
PREDICT_URL = LOCAL_SERVER_URL + '/predictions'
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# defaults from https://huggingface.co/blog/stable_diffusion
|
||||
class ImageRequest(BaseModel):
|
||||
prompt: str
|
||||
init_image: str = None # base64
|
||||
mask: str = None # base64
|
||||
num_outputs: str = "1"
|
||||
num_inference_steps: str = "50"
|
||||
guidance_scale: str = "7.5"
|
||||
width: str = "512"
|
||||
height: str = "512"
|
||||
seed: str = "30000"
|
||||
prompt_strength: str = "0.8"
|
||||
|
||||
@app.get('/')
|
||||
def read_root():
|
||||
return FileResponse('index.html')
|
||||
|
||||
@app.get('/ping')
|
||||
async def ping():
|
||||
try:
|
||||
requests.get(LOCAL_SERVER_URL)
|
||||
return {'OK'}
|
||||
except:
|
||||
return {'ERROR'}
|
||||
|
||||
@app.post('/image')
|
||||
async def image(req : ImageRequest):
|
||||
data = {
|
||||
"input": {
|
||||
"prompt": req.prompt,
|
||||
"num_outputs": req.num_outputs,
|
||||
"num_inference_steps": req.num_inference_steps,
|
||||
"width": req.width,
|
||||
"height": req.height,
|
||||
"seed": req.seed,
|
||||
"guidance_scale": req.guidance_scale,
|
||||
}
|
||||
}
|
||||
|
||||
if req.init_image is not None:
|
||||
data['input']['init_image'] = req.init_image
|
||||
data['input']['prompt_strength'] = req.prompt_strength
|
||||
|
||||
if req.mask is not None:
|
||||
data['input']['mask'] = req.mask
|
||||
|
||||
if req.seed == "-1":
|
||||
del data['input']['seed']
|
||||
|
||||
res = requests.post(PREDICT_URL, json=data)
|
||||
if res.status_code != 200:
|
||||
raise HTTPException(status_code=500, detail=res.text)
|
||||
|
||||
return res.json()
|
||||
|
||||
@app.get('/media/ding.mp3')
|
||||
def read_root():
|
||||
return FileResponse('media/ding.mp3')
|
Before Width: | Height: | Size: 18 KiB |
BIN
media/config-v3.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
media/config-v4.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
media/config-v5.jpg
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
media/config-v6.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
media/config-v6.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
media/config-v7.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
media/download buttons.xcf
Normal file
BIN
media/download-linux.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
media/download-win.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
media/modifiers-v1.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
media/shot-v10-simple.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
media/shot-v10.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
media/shot-v8.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
BIN
media/shot-v9.jpg
Normal file
After Width: | Height: | Size: 199 KiB |
BIN
media/system-settings-v2.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
media/task-queue-v1.jpg
Normal file
After Width: | Height: | Size: 155 KiB |
@ -1,38 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/', response_class=HTMLResponse)
|
||||
def read_root():
|
||||
return '''
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial;
|
||||
font-size: 11pt;
|
||||
}
|
||||
pre {
|
||||
display: inline;
|
||||
background: #aaa;
|
||||
padding: 2px;
|
||||
border: 1px solid #777;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
pre {
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h4>The UI has moved to <a href="http://localhost:9000">http://localhost:9000</a>. The current address that you used (ending with :8000) will be removed in the future, so please use <a href="http://localhost:9000">http://localhost:9000</a> going ahead (and in any bookmarks you've saved).</h4>
|
||||
|
||||
<h4>Also, please use <pre>./server</pre> instead of <pre>docker-compose up &</pre>. To stop, please use <pre>./server stop</pre>. This will help the project better manage the startup process in the future.</h4>
|
||||
|
||||
<h3>Why has the address changed?</h3>
|
||||
<p>The previously used port (8000) is often used by other servers, which results in port conflicts. So the project's port number has been changed, while the project is still young. Otherwise port-conflicts with 8000 will be a common source of new-user issues in the future.</p>
|
||||
<p>Sorry about this, and apologies for the inconvenience :)</p>
|
||||
'''
|
@ -1,3 +0,0 @@
|
||||
requests
|
||||
fastapi==0.80.0
|
||||
uvicorn==0.18.2
|
43
scripts/Developer Console.cmd
Normal file
@ -0,0 +1,43 @@
|
||||
@echo off
|
||||
|
||||
echo "Opening Stable Diffusion UI - Developer Console.." & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@rem set legacy and new installer's PATH, if they exist
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
|
||||
@rem Test the environment
|
||||
echo "Environment Info:"
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
|
||||
echo.
|
||||
|
||||
@rem activate the legacy environment (if present) and set PYTHONPATH
|
||||
if exist "installer_files\env" (
|
||||
set PYTHONPATH=%cd%\installer_files\env\lib\site-packages
|
||||
)
|
||||
if exist "stable-diffusion\env" (
|
||||
call conda activate .\stable-diffusion\env
|
||||
set PYTHONPATH=%cd%\stable-diffusion\env\lib\site-packages
|
||||
)
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
@rem done
|
||||
echo.
|
||||
|
||||
cmd /k
|
46
scripts/Start Stable Diffusion UI.cmd
Normal file
@ -0,0 +1,46 @@
|
||||
@echo off
|
||||
|
||||
cd /d %~dp0
|
||||
echo Install dir: %~dp0
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
if exist "on_sd_start.bat" (
|
||||
echo ================================================================================
|
||||
echo.
|
||||
echo !!!! WARNING !!!!
|
||||
echo.
|
||||
echo It looks like you're trying to run the installation script from a source code
|
||||
echo download. This will not work.
|
||||
echo.
|
||||
echo Recommended: Please close this window and download the installer from
|
||||
echo https://stable-diffusion-ui.github.io/docs/installation/
|
||||
echo.
|
||||
echo ================================================================================
|
||||
echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@rem set legacy installer's PATH, if it exists
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
|
||||
@rem Setup the packages required for the installer
|
||||
call scripts\bootstrap.bat
|
||||
|
||||
@rem set new installer's PATH, if it downloaded any packages
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem Test the bootstrap
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
|
||||
@rem Download the rest of the installer and UI
|
||||
call scripts\on_env_start.bat
|
||||
|
||||
@pause
|
78
scripts/bootstrap.bat
Normal file
@ -0,0 +1,78 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
@rem This script will install git and conda (if not found on the PATH variable)
|
||||
@rem using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
@rem For users who already have git and conda, this step will be skipped.
|
||||
|
||||
@rem This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
@rem config
|
||||
set MAMBA_ROOT_PREFIX=%cd%\installer_files\mamba
|
||||
set INSTALL_ENV_DIR=%cd%\installer_files\env
|
||||
set LEGACY_INSTALL_ENV_DIR=%cd%\installer
|
||||
set MICROMAMBA_DOWNLOAD_URL=https://github.com/cmdr2/stable-diffusion-ui/releases/download/v1.1/micromamba.exe
|
||||
set umamba_exists=F
|
||||
|
||||
set OLD_APPDATA=%APPDATA%
|
||||
set OLD_USERPROFILE=%USERPROFILE%
|
||||
set APPDATA=%cd%\installer_files\appdata
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
@rem figure out whether git and conda needs to be installed
|
||||
if exist "%INSTALL_ENV_DIR%" set PATH=%INSTALL_ENV_DIR%;%INSTALL_ENV_DIR%\Library\bin;%INSTALL_ENV_DIR%\Scripts;%INSTALL_ENV_DIR%\Library\usr\bin;%PATH%
|
||||
|
||||
set PACKAGES_TO_INSTALL=
|
||||
|
||||
if not exist "%LEGACY_INSTALL_ENV_DIR%\etc\profile.d\conda.sh" (
|
||||
if not exist "%INSTALL_ENV_DIR%\etc\profile.d\conda.sh" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% conda python=3.8.5
|
||||
)
|
||||
|
||||
call git --version >.tmp1 2>.tmp2
|
||||
if "!ERRORLEVEL!" NEQ "0" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% git
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version >.tmp1 2>.tmp2
|
||||
if "!ERRORLEVEL!" EQU "0" set umamba_exists=T
|
||||
|
||||
@rem (if necessary) install git and conda into a contained environment
|
||||
if "%PACKAGES_TO_INSTALL%" NEQ "" (
|
||||
@rem download micromamba
|
||||
if "%umamba_exists%" == "F" (
|
||||
echo "Downloading micromamba from %MICROMAMBA_DOWNLOAD_URL% to %MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
mkdir "%MAMBA_ROOT_PREFIX%"
|
||||
call curl -Lk "%MICROMAMBA_DOWNLOAD_URL%" > "%MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
if "!ERRORLEVEL!" NEQ "0" (
|
||||
echo "There was a problem downloading micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
mkdir "%APPDATA%"
|
||||
mkdir "%USERPROFILE%"
|
||||
|
||||
@rem test the mamba binary
|
||||
echo Micromamba version:
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version
|
||||
)
|
||||
|
||||
@rem create the installer env
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" create -y --prefix "%INSTALL_ENV_DIR%"
|
||||
)
|
||||
|
||||
echo "Packages to install:%PACKAGES_TO_INSTALL%"
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" install -y --prefix "%INSTALL_ENV_DIR%" -c conda-forge %PACKAGES_TO_INSTALL%
|
||||
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
echo "There was a problem while installing%PACKAGES_TO_INSTALL% using micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@rem revert to the old APPDATA. only needed it for bypassing a bug in micromamba (with special characters)
|
||||
set APPDATA=%OLD_APPDATA%
|
||||
set USERPROFILE=%OLD_USERPROFILE%
|
96
scripts/bootstrap.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will install git and conda (if not found on the PATH variable)
|
||||
# using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
# For users who already have git and conda, this step will be skipped.
|
||||
|
||||
# This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
set -o pipefail
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
case "${OS_NAME}" in
|
||||
Linux*) OS_NAME="linux";;
|
||||
Darwin*) OS_NAME="osx";;
|
||||
*) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit
|
||||
esac
|
||||
|
||||
OS_ARCH=$(uname -m)
|
||||
case "${OS_ARCH}" in
|
||||
x86_64*) OS_ARCH="64";;
|
||||
arm64*) OS_ARCH="arm64";;
|
||||
aarch64*) OS_ARCH="arm64";;
|
||||
*) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit
|
||||
esac
|
||||
|
||||
if ! which curl; then fail "'curl' not found. Please install curl."; fi
|
||||
if ! which tar; then fail "'tar' not found. Please install tar."; fi
|
||||
if ! which bzip2; then fail "'bzip2' not found. Please install bzip2."; fi
|
||||
|
||||
if pwd | grep ' '; then fail "The installation directory's path contains a space character. Conda will fail to install. Please change the directory."; fi
|
||||
if [ -f /proc/cpuinfo ]; then
|
||||
if ! cat /proc/cpuinfo | grep avx | uniq; then fail "Your CPU doesn't support AVX."; fi
|
||||
fi
|
||||
|
||||
# https://mamba.readthedocs.io/en/latest/installation.html
|
||||
if [ "$OS_NAME" == "linux" ] && [ "$OS_ARCH" == "arm64" ]; then OS_ARCH="aarch64"; fi
|
||||
|
||||
# config
|
||||
export MAMBA_ROOT_PREFIX="$(pwd)/installer_files/mamba"
|
||||
INSTALL_ENV_DIR="$(pwd)/installer_files/env"
|
||||
LEGACY_INSTALL_ENV_DIR="$(pwd)/installer"
|
||||
MICROMAMBA_DOWNLOAD_URL="https://micro.mamba.pm/api/micromamba/${OS_NAME}-${OS_ARCH}/latest"
|
||||
umamba_exists="F"
|
||||
|
||||
# figure out whether git and conda needs to be installed
|
||||
if [ -e "$INSTALL_ENV_DIR" ]; then export PATH="$INSTALL_ENV_DIR/bin:$PATH"; fi
|
||||
|
||||
PACKAGES_TO_INSTALL=""
|
||||
|
||||
if [ ! -e "$LEGACY_INSTALL_ENV_DIR/etc/profile.d/conda.sh" ] && [ ! -e "$INSTALL_ENV_DIR/etc/profile.d/conda.sh" ]; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL conda python=3.8.5"; fi
|
||||
if ! hash "git" &>/dev/null; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL git"; fi
|
||||
|
||||
if "$MAMBA_ROOT_PREFIX/micromamba" --version &>/dev/null; then umamba_exists="T"; fi
|
||||
|
||||
# (if necessary) install git and conda into a contained environment
|
||||
if [ "$PACKAGES_TO_INSTALL" != "" ]; then
|
||||
# download micromamba
|
||||
if [ "$umamba_exists" == "F" ]; then
|
||||
echo "Downloading micromamba from $MICROMAMBA_DOWNLOAD_URL to $MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
mkdir -p "$MAMBA_ROOT_PREFIX"
|
||||
curl -L "$MICROMAMBA_DOWNLOAD_URL" | tar -xvj -O bin/micromamba > "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo
|
||||
echo "EE micromamba download failed"
|
||||
echo "EE If the lines above contain 'bzip2: Cannot exec', your system doesn't have bzip2 installed"
|
||||
echo "EE If there are network errors, please check your internet setup"
|
||||
fail "micromamba download failed"
|
||||
fi
|
||||
|
||||
chmod u+x "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
# test the mamba binary
|
||||
echo "Micromamba version:"
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" --version
|
||||
fi
|
||||
|
||||
# create the installer env
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" create -y --prefix "$INSTALL_ENV_DIR" || fail "unable to create the install environment"
|
||||
fi
|
||||
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
fail "There was a problem while installing$PACKAGES_TO_INSTALL using micromamba. Cannot continue."
|
||||
fi
|
||||
|
||||
echo "Packages to install:$PACKAGES_TO_INSTALL"
|
||||
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" install -y --prefix "$INSTALL_ENV_DIR" -c conda-forge $PACKAGES_TO_INSTALL
|
||||
if [ "$?" != "0" ]; then
|
||||
fail "Installation of the packages '$PACKAGES_TO_INSTALL' failed."
|
||||
fi
|
||||
fi
|
13
scripts/check_modules.py
Normal file
@ -0,0 +1,13 @@
|
||||
'''
|
||||
This script checks if the given modules exist
|
||||
'''
|
||||
|
||||
import sys
|
||||
import pkgutil
|
||||
|
||||
modules = sys.argv[1:]
|
||||
missing_modules = []
|
||||
for m in modules:
|
||||
if pkgutil.find_loader(m) is None:
|
||||
print('module', m, 'not found')
|
||||
exit(1)
|
53
scripts/developer_console.sh
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ "$0" == "bash" ]; then
|
||||
echo "Opening Stable Diffusion UI - Developer Console.."
|
||||
echo ""
|
||||
|
||||
# set legacy and new installer's PATH, if they exist
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate
|
||||
|
||||
# test the environment
|
||||
echo "Environment Info:"
|
||||
which git
|
||||
git --version
|
||||
|
||||
which conda
|
||||
conda --version
|
||||
|
||||
echo ""
|
||||
|
||||
# activate the legacy environment (if present) and set PYTHONPATH
|
||||
if [ -e "installer_files/env" ]; then
|
||||
export PYTHONPATH="$(pwd)/installer_files/env/lib/python3.8/site-packages"
|
||||
fi
|
||||
if [ -e "stable-diffusion/env" ]; then
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script)
|
||||
|
||||
conda activate ./stable-diffusion/env
|
||||
|
||||
export PYTHONPATH="$(pwd)/stable-diffusion/env/lib/python3.8/site-packages"
|
||||
fi
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
|
||||
# done
|
||||
|
||||
echo ""
|
||||
else
|
||||
file_name=$(basename "${BASH_SOURCE[0]}")
|
||||
bash --init-file "$file_name"
|
||||
fi
|
39
scripts/functions.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# utility functions for all scripts
|
||||
#
|
||||
|
||||
fail() {
|
||||
echo
|
||||
echo "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
|
||||
echo
|
||||
if [ "$1" != "" ]; then
|
||||
echo ERROR: $1
|
||||
else
|
||||
echo An error occurred.
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
Error downloading Stable Diffusion UI. Sorry about that, please try to:
|
||||
1. Run this installer again.
|
||||
2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
||||
3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB
|
||||
4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
EOF
|
||||
read -p "Press any key to continue"
|
||||
exit 1
|
||||
|
||||
}
|
||||
|
||||
filesize() {
|
||||
case "$(uname -s)" in
|
||||
Linux*) stat -c "%s" $1;;
|
||||
Darwin*) stat -f "%z" $1;;
|
||||
*) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit
|
||||
esac
|
||||
}
|
||||
|
||||
|
62
scripts/on_env_start.bat
Normal file
@ -0,0 +1,62 @@
|
||||
@echo off
|
||||
|
||||
@echo. & echo "Easy Diffusion - v2" & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
if exist "scripts\config.bat" (
|
||||
@call scripts\config.bat
|
||||
)
|
||||
|
||||
if "%update_branch%"=="" (
|
||||
set update_branch=main
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
for /f "tokens=*" %%a in ('python -c "import os; parts = os.getcwd().split(os.path.sep); print(len(parts))"') do if "%%a" NEQ "2" (
|
||||
echo. & echo "!!!! WARNING !!!!" & echo.
|
||||
echo "Your 'stable-diffusion-ui' folder is at %cd%" & echo.
|
||||
echo "The 'stable-diffusion-ui' folder needs to be at the top of your drive, for e.g. 'C:\stable-diffusion-ui' or 'D:\stable-diffusion-ui' etc."
|
||||
echo "Not placing this folder at the top of a drive can cause errors on some computers."
|
||||
echo. & echo "Recommended: Please close this window and move the 'stable-diffusion-ui' folder to the top of a drive. For e.g. 'C:\stable-diffusion-ui'. Then run the installer again." & echo.
|
||||
echo "Not Recommended: If you're sure that you want to install at the current location, please press any key to continue." & echo.
|
||||
|
||||
pause
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_ui_git_cloned" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Easy Diffusion's git repository was already installed. Updating from %update_branch%.."
|
||||
|
||||
@cd sd-ui-files
|
||||
|
||||
@call git reset --hard
|
||||
@call git -c advice.detachedHead=false checkout "%update_branch%"
|
||||
@call git pull
|
||||
|
||||
@cd ..
|
||||
) else (
|
||||
@echo. & echo "Downloading Easy Diffusion..." & echo.
|
||||
@echo "Using the %update_branch% channel" & echo.
|
||||
|
||||
@call git clone -b "%update_branch%" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files && (
|
||||
@echo sd_ui_git_cloned >> scripts\install_status.txt
|
||||
) || (
|
||||
@echo "Error downloading Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
@exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@xcopy sd-ui-files\ui ui /s /i /Y /q
|
||||
@copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\check_modules.py scripts\ /Y
|
||||
@copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
@copy "sd-ui-files\scripts\Developer Console.cmd" . /Y
|
||||
|
||||
@call scripts\on_sd_start.bat
|
||||
|
||||
@pause
|
45
scripts/on_env_start.sh
Executable file
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
printf "\n\nEasy Diffusion\n\n"
|
||||
|
||||
if [ -f "scripts/config.sh" ]; then
|
||||
source scripts/config.sh
|
||||
fi
|
||||
|
||||
if [ "$update_branch" == "" ]; then
|
||||
export update_branch="main"
|
||||
fi
|
||||
|
||||
if [ -f "scripts/install_status.txt" ] && [ `grep -c sd_ui_git_cloned scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Easy Diffusion's git repository was already installed. Updating from $update_branch.."
|
||||
|
||||
cd sd-ui-files
|
||||
|
||||
git reset --hard
|
||||
git -c advice.detachedHead=false checkout "$update_branch"
|
||||
git pull
|
||||
|
||||
cd ..
|
||||
else
|
||||
printf "\n\nDownloading Easy Diffusion..\n\n"
|
||||
printf "Using the $update_branch channel\n\n"
|
||||
|
||||
if git clone -b "$update_branch" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files ; then
|
||||
echo sd_ui_git_cloned >> scripts/install_status.txt
|
||||
else
|
||||
fail "git clone failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf ui
|
||||
cp -Rf sd-ui-files/ui .
|
||||
cp sd-ui-files/scripts/on_sd_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
cp sd-ui-files/scripts/check_modules.py scripts/
|
||||
cp sd-ui-files/scripts/start.sh .
|
||||
cp sd-ui-files/scripts/developer_console.sh .
|
||||
cp sd-ui-files/scripts/functions.sh scripts/
|
||||
|
||||
exec ./scripts/on_sd_start.sh
|
356
scripts/on_sd_start.bat
Normal file
@ -0,0 +1,356 @@
|
||||
@echo off
|
||||
|
||||
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\check_modules.py scripts\ /Y
|
||||
|
||||
if exist "%cd%\profile" (
|
||||
set USERPROFILE=%cd%\profile
|
||||
)
|
||||
|
||||
@rem set the correct installer path (current vs legacy)
|
||||
if exist "%cd%\installer_files\env" (
|
||||
set INSTALL_ENV_DIR=%cd%\installer_files\env
|
||||
)
|
||||
if exist "%cd%\stable-diffusion\env" (
|
||||
set INSTALL_ENV_DIR=%cd%\stable-diffusion\env
|
||||
)
|
||||
|
||||
@mkdir tmp
|
||||
@set TMP=%cd%\tmp
|
||||
@set TEMP=%cd%\tmp
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "Error activating conda for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@REM remove the old version of the dev console script, if it's still present
|
||||
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
|
||||
@call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
|
||||
|
||||
@rem create the stable-diffusion folder, to work with legacy installations
|
||||
if not exist "stable-diffusion" mkdir stable-diffusion
|
||||
cd stable-diffusion
|
||||
|
||||
@rem activate the old stable-diffusion env, if it exists
|
||||
if exist "env" (
|
||||
call conda activate .\env
|
||||
)
|
||||
|
||||
@rem disable the legacy src and ldm folder (otherwise this prevents installing gfpgan and realesrgan)
|
||||
if exist src rename src src-old
|
||||
if exist ldm rename ldm ldm-old
|
||||
|
||||
if not exist "..\models\stable-diffusion" mkdir "..\models\stable-diffusion"
|
||||
if not exist "..\models\gfpgan" mkdir "..\models\gfpgan"
|
||||
if not exist "..\models\realesrgan" mkdir "..\models\realesrgan"
|
||||
if not exist "..\models\vae" mkdir "..\models\vae"
|
||||
|
||||
@rem migrate the legacy models to the correct path (if already downloaded)
|
||||
if exist "sd-v1-4.ckpt" move sd-v1-4.ckpt ..\models\stable-diffusion\
|
||||
if exist "custom-model.ckpt" move custom-model.ckpt ..\models\stable-diffusion\
|
||||
if exist "GFPGANv1.3.pth" move GFPGANv1.3.pth ..\models\gfpgan\
|
||||
if exist "RealESRGAN_x4plus.pth" move RealESRGAN_x4plus.pth ..\models\realesrgan\
|
||||
if exist "RealESRGAN_x4plus_anime_6B.pth" move RealESRGAN_x4plus_anime_6B.pth ..\models\realesrgan\
|
||||
|
||||
if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libssl-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
|
||||
if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
|
||||
|
||||
@rem install torch and torchvision
|
||||
call python ..\scripts\check_modules.py torch torchvision
|
||||
if "%ERRORLEVEL%" EQU "0" (
|
||||
echo "torch and torchvision have already been installed."
|
||||
) else (
|
||||
echo "Installing torch and torchvision.."
|
||||
|
||||
@REM prevent from using packages from the user's home directory, to avoid conflicts
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
call python -m pip install --upgrade torch torchvision --extra-index-url https://download.pytorch.org/whl/cu116 || (
|
||||
echo "Error installing torch. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@rem install/upgrade sdkit
|
||||
call python ..\scripts\check_modules.py sdkit sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan
|
||||
if "%ERRORLEVEL%" EQU "0" (
|
||||
echo "sdkit is already installed."
|
||||
|
||||
@rem skip sdkit upgrade if in developer-mode
|
||||
if not exist "..\src\sdkit" (
|
||||
@REM prevent from using packages from the user's home directory, to avoid conflicts
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
call python -m pip install --upgrade sdkit==1.0.43 -q || (
|
||||
echo "Error updating sdkit"
|
||||
)
|
||||
)
|
||||
) else (
|
||||
echo "Installing sdkit: https://pypi.org/project/sdkit/"
|
||||
|
||||
@REM prevent from using packages from the user's home directory, to avoid conflicts
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
call python -m pip install sdkit==1.0.43 || (
|
||||
echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
call python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))"
|
||||
|
||||
@rem upgrade stable-diffusion-sdkit
|
||||
call python -m pip install --upgrade stable-diffusion-sdkit==2.1.3 -q || (
|
||||
echo "Error updating stable-diffusion-sdkit"
|
||||
)
|
||||
call python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))"
|
||||
|
||||
@rem install rich
|
||||
call python ..\scripts\check_modules.py rich
|
||||
if "%ERRORLEVEL%" EQU "0" (
|
||||
echo "rich has already been installed."
|
||||
) else (
|
||||
echo "Installing rich.."
|
||||
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
call python -m pip install rich || (
|
||||
echo "Error installing rich. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
call python ..\scripts\check_modules.py uvicorn fastapi
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
echo "Packages necessary for Easy Diffusion were already installed"
|
||||
) else (
|
||||
@echo. & echo "Downloading packages necessary for Easy Diffusion..." & echo.
|
||||
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
@call conda install -c conda-forge -y uvicorn fastapi || (
|
||||
echo "Error installing the packages necessary for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
call WHERE uvicorn > .tmp
|
||||
@>nul findstr /m "uvicorn" .tmp
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@if exist "..\models\stable-diffusion\sd-v1-4.ckpt" (
|
||||
for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" EQU "4265380512" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 4 GB Model."
|
||||
) else (
|
||||
for %%J in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zJ" EQU "7703807346" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 7 GB Model."
|
||||
) else (
|
||||
for %%K in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zK" EQU "7703810927" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the Waifu Model."
|
||||
) else (
|
||||
echo. & echo "The model file present at models\stable-diffusion\sd-v1-4.ckpt is invalid. It is only %%~zK bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\stable-diffusion\sd-v1-4.ckpt"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\stable-diffusion\sd-v1-4.ckpt" (
|
||||
@echo. & echo "Downloading data files (weights) for Stable Diffusion.." & echo.
|
||||
|
||||
@call curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ..\models\stable-diffusion\sd-v1-4.ckpt
|
||||
|
||||
@if exist "..\models\stable-diffusion\sd-v1-4.ckpt" (
|
||||
for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" NEQ "4265380512" (
|
||||
echo. & echo "Error: The downloaded model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "..\models\gfpgan\GFPGANv1.3.pth" (
|
||||
for %%I in ("..\models\gfpgan\GFPGANv1.3.pth") do if "%%~zI" EQU "348632874" (
|
||||
echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The GFPGAN model file present at models\gfpgan\GFPGANv1.3.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\gfpgan\GFPGANv1.3.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\gfpgan\GFPGANv1.3.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for GFPGAN (Face Correction).." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > ..\models\gfpgan\GFPGANv1.3.pth
|
||||
|
||||
@if exist "..\models\gfpgan\GFPGANv1.3.pth" (
|
||||
for %%I in ("..\models\gfpgan\GFPGANv1.3.pth") do if "%%~zI" NEQ "348632874" (
|
||||
echo. & echo "Error: The downloaded GFPGAN model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "..\models\realesrgan\RealESRGAN_x4plus.pth" (
|
||||
for %%I in ("..\models\realesrgan\RealESRGAN_x4plus.pth") do if "%%~zI" EQU "67040989" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The RealESRGAN model file present at models\realesrgan\RealESRGAN_x4plus.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\realesrgan\RealESRGAN_x4plus.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\realesrgan\RealESRGAN_x4plus.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > ..\models\realesrgan\RealESRGAN_x4plus.pth
|
||||
|
||||
@if exist "..\models\realesrgan\RealESRGAN_x4plus.pth" (
|
||||
for %%I in ("..\models\realesrgan\RealESRGAN_x4plus.pth") do if "%%~zI" NEQ "67040989" (
|
||||
echo. & echo "Error: The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" (
|
||||
for %%I in ("..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" EQU "17938799" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The RealESRGAN model file present at models\realesrgan\RealESRGAN_x4plus_anime_6B.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > ..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth
|
||||
|
||||
@if exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" (
|
||||
for %%I in ("..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" NEQ "17938799" (
|
||||
echo. & echo "Error: The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" EQU "334695179" (
|
||||
echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The default VAE (sd-vae-ft-mse-original) file present at models\vae\vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
@echo. & echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).." & echo.
|
||||
|
||||
@call curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt
|
||||
|
||||
@if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" NEQ "334695179" (
|
||||
echo. & echo "Error: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo sd_weights_downloaded >> ..\scripts\install_status.txt
|
||||
@echo sd_install_complete >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@echo. & echo "Easy Diffusion installation complete! Starting the server!" & echo.
|
||||
|
||||
@set SD_DIR=%cd%
|
||||
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
@cd ..
|
||||
@set SD_UI_PATH=%cd%\ui
|
||||
@cd stable-diffusion
|
||||
|
||||
@rem set any overrides
|
||||
set HF_HUB_DISABLE_SYMLINKS_WARNING=true
|
||||
|
||||
@if NOT DEFINED SD_UI_BIND_PORT set SD_UI_BIND_PORT=9000
|
||||
@if NOT DEFINED SD_UI_BIND_IP set SD_UI_BIND_IP=0.0.0.0
|
||||
@uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %SD_UI_BIND_PORT% --host %SD_UI_BIND_IP% --log-level error
|
||||
|
||||
|
||||
@pause
|
300
scripts/on_sd_start.sh
Executable file
@ -0,0 +1,300 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp sd-ui-files/scripts/functions.sh scripts/
|
||||
cp sd-ui-files/scripts/on_env_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
cp sd-ui-files/scripts/check_modules.py scripts/
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate || fail "Failed to activate conda"
|
||||
|
||||
# remove the old version of the dev console script, if it's still present
|
||||
if [ -e "open_dev_console.sh" ]; then
|
||||
rm "open_dev_console.sh"
|
||||
fi
|
||||
|
||||
python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
|
||||
|
||||
# Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
# Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
# set the correct installer path (current vs legacy)
|
||||
if [ -e "installer_files/env" ]; then
|
||||
export INSTALL_ENV_DIR="$(pwd)/installer_files/env"
|
||||
fi
|
||||
if [ -e "stable-diffusion/env" ]; then
|
||||
export INSTALL_ENV_DIR="$(pwd)/stable-diffusion/env"
|
||||
fi
|
||||
|
||||
# create the stable-diffusion folder, to work with legacy installations
|
||||
if [ ! -e "stable-diffusion" ]; then mkdir stable-diffusion; fi
|
||||
cd stable-diffusion
|
||||
|
||||
# activate the old stable-diffusion env, if it exists
|
||||
if [ -e "env" ]; then
|
||||
conda activate ./env || fail "conda activate failed"
|
||||
fi
|
||||
|
||||
# disable the legacy src and ldm folder (otherwise this prevents installing gfpgan and realesrgan)
|
||||
if [ -e "src" ]; then mv src src-old; fi
|
||||
if [ -e "ldm" ]; then mv ldm ldm-old; fi
|
||||
|
||||
mkdir -p "../models/stable-diffusion"
|
||||
mkdir -p "../models/gfpgan"
|
||||
mkdir -p "../models/realesrgan"
|
||||
mkdir -p "../models/vae"
|
||||
|
||||
# migrate the legacy models to the correct path (if already downloaded)
|
||||
if [ -e "sd-v1-4.ckpt" ]; then mv sd-v1-4.ckpt ../models/stable-diffusion/; fi
|
||||
if [ -e "custom-model.ckpt" ]; then mv custom-model.ckpt ../models/stable-diffusion/; fi
|
||||
if [ -e "GFPGANv1.3.pth" ]; then mv GFPGANv1.3.pth ../models/gfpgan/; fi
|
||||
if [ -e "RealESRGAN_x4plus.pth" ]; then mv RealESRGAN_x4plus.pth ../models/realesrgan/; fi
|
||||
if [ -e "RealESRGAN_x4plus_anime_6B.pth" ]; then mv RealESRGAN_x4plus_anime_6B.pth ../models/realesrgan/; fi
|
||||
|
||||
# install torch and torchvision
|
||||
if python ../scripts/check_modules.py torch torchvision; then
|
||||
echo "torch and torchvision have already been installed."
|
||||
else
|
||||
echo "Installing torch and torchvision.."
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
|
||||
if python -m pip install --upgrade torch torchvision --extra-index-url https://download.pytorch.org/whl/cu116 ; then
|
||||
echo "Installed."
|
||||
else
|
||||
fail "torch install failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# install/upgrade sdkit
|
||||
if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan ; then
|
||||
echo "sdkit is already installed."
|
||||
|
||||
# skip sdkit upgrade if in developer-mode
|
||||
if [ ! -e "../src/sdkit" ]; then
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
|
||||
python -m pip install --upgrade sdkit==1.0.43 -q
|
||||
fi
|
||||
else
|
||||
echo "Installing sdkit: https://pypi.org/project/sdkit/"
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
|
||||
if python -m pip install sdkit==1.0.43 ; then
|
||||
echo "Installed."
|
||||
else
|
||||
fail "sdkit install failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))"
|
||||
|
||||
# upgrade stable-diffusion-sdkit
|
||||
python -m pip install --upgrade stable-diffusion-sdkit==2.1.3 -q
|
||||
python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))"
|
||||
|
||||
# install rich
|
||||
if python ../scripts/check_modules.py rich; then
|
||||
echo "rich has already been installed."
|
||||
else
|
||||
echo "Installing rich.."
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
|
||||
if python -m pip install rich ; then
|
||||
echo "Installed."
|
||||
else
|
||||
fail "Install failed for rich"
|
||||
fi
|
||||
fi
|
||||
|
||||
if python ../scripts/check_modules.py uvicorn fastapi ; then
|
||||
echo "Packages necessary for Easy Diffusion were already installed"
|
||||
else
|
||||
printf "\n\nDownloading packages necessary for Easy Diffusion..\n\n"
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
|
||||
if conda install -c conda-forge -y uvicorn fastapi ; then
|
||||
echo "Installed. Testing.."
|
||||
else
|
||||
fail "'conda install uvicorn' failed"
|
||||
fi
|
||||
|
||||
if ! command -v uvicorn &> /dev/null; then
|
||||
fail "UI packages not found!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then
|
||||
model_size=`filesize "../models/stable-diffusion/sd-v1-4.ckpt"`
|
||||
|
||||
if [ "$model_size" -eq "4265380512" ] || [ "$model_size" -eq "7703807346" ] || [ "$model_size" -eq "7703810927" ]; then
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/stable-diffusion/sd-v1-4.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/stable-diffusion/sd-v1-4.ckpt
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then
|
||||
echo "Downloading data files (weights) for Stable Diffusion.."
|
||||
|
||||
curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ../models/stable-diffusion/sd-v1-4.ckpt
|
||||
|
||||
if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then
|
||||
model_size=`filesize "../models/stable-diffusion/sd-v1-4.ckpt"`
|
||||
if [ ! "$model_size" == "4265380512" ]; then
|
||||
fail "The downloaded model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for Stable Diffusion"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "../models/gfpgan/GFPGANv1.3.pth" ]; then
|
||||
model_size=`filesize "../models/gfpgan/GFPGANv1.3.pth"`
|
||||
|
||||
if [ "$model_size" -eq "348632874" ]; then
|
||||
echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/gfpgan/GFPGANv1.3.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/gfpgan/GFPGANv1.3.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/gfpgan/GFPGANv1.3.pth" ]; then
|
||||
echo "Downloading data files (weights) for GFPGAN (Face Correction).."
|
||||
|
||||
curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > ../models/gfpgan/GFPGANv1.3.pth
|
||||
|
||||
if [ -f "../models/gfpgan/GFPGANv1.3.pth" ]; then
|
||||
model_size=`filesize "../models/gfpgan/GFPGANv1.3.pth"`
|
||||
if [ ! "$model_size" -eq "348632874" ]; then
|
||||
fail "The downloaded GFPGAN model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for GFPGAN (Face Correction)."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then
|
||||
model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus.pth"`
|
||||
|
||||
if [ "$model_size" -eq "67040989" ]; then
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/realesrgan/RealESRGAN_x4plus.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/realesrgan/RealESRGAN_x4plus.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then
|
||||
echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.."
|
||||
|
||||
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > ../models/realesrgan/RealESRGAN_x4plus.pth
|
||||
|
||||
if [ -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then
|
||||
model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus.pth"`
|
||||
if [ ! "$model_size" -eq "67040989" ]; then
|
||||
fail "The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth"`
|
||||
|
||||
if [ "$model_size" -eq "17938799" ]; then
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/realesrgan/RealESRGAN_x4plus_anime_6B.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.."
|
||||
|
||||
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > ../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth
|
||||
|
||||
if [ -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth"`
|
||||
if [ ! "$model_size" -eq "17938799" ]; then
|
||||
fail "The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
model_size=`filesize "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt"`
|
||||
|
||||
if [ "$model_size" -eq "334695179" ]; then
|
||||
echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/vae/vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).."
|
||||
|
||||
curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt
|
||||
|
||||
if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
model_size=`filesize "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt"`
|
||||
if [ ! "$model_size" -eq "334695179" ]; then
|
||||
printf "\n\nError: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: $model_size\n\n"
|
||||
printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
|
||||
read -p "Press any key to continue"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
|
||||
read -p "Press any key to continue"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo sd_weights_downloaded >> ../scripts/install_status.txt
|
||||
echo sd_install_complete >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
printf "\n\nEasy Diffusion installation complete, starting the server!\n\n"
|
||||
|
||||
SD_PATH=`pwd`
|
||||
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
cd ..
|
||||
export SD_UI_PATH=`pwd`/ui
|
||||
cd stable-diffusion
|
||||
|
||||
uvicorn main:server_api --app-dir "$SD_UI_PATH" --port ${SD_UI_BIND_PORT:-9000} --host ${SD_UI_BIND_IP:-0.0.0.0} --log-level error
|
||||
|
||||
read -p "Press any key to continue"
|
41
scripts/start.sh
Executable file
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ -f "on_sd_start.bat" ]; then
|
||||
echo ================================================================================
|
||||
echo
|
||||
echo !!!! WARNING !!!!
|
||||
echo
|
||||
echo It looks like you\'re trying to run the installation script from a source code
|
||||
echo download. This will not work.
|
||||
echo
|
||||
echo Recommended: Please close this window and download the installer from
|
||||
echo https://stable-diffusion-ui.github.io/docs/installation/
|
||||
echo
|
||||
echo ================================================================================
|
||||
echo
|
||||
read
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# set legacy installer's PATH, if it exists
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
|
||||
# Setup the packages required for the installer
|
||||
scripts/bootstrap.sh || exit 1
|
||||
|
||||
# set new installer's PATH, if it downloaded any packages
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# Test the bootstrap
|
||||
which git
|
||||
git --version || exit 1
|
||||
|
||||
which conda
|
||||
conda --version || exit 1
|
||||
|
||||
# Download the rest of the installer and UI
|
||||
chmod +x scripts/*.sh
|
||||
scripts/on_env_start.sh
|
2
scripts/win_enable_long_filepaths.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Type DWord -Value 1
|
||||
pause
|
26
server
@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CMD="$1"
|
||||
if [ -z "$1" ]; then
|
||||
CMD="start"
|
||||
fi
|
||||
|
||||
start_server() {
|
||||
docker-compose up -d stable-diffusion-old-port-redirect > /dev/null 2>&1 # old port 8000 server, show redirect notice
|
||||
docker-compose up stability-ai stable-diffusion-ui &
|
||||
}
|
||||
|
||||
stop_server() {
|
||||
docker-compose down
|
||||
}
|
||||
|
||||
if [ "$CMD" == "start" ]; then
|
||||
start_server
|
||||
elif [ "$CMD" == "stop" ]; then
|
||||
stop_server
|
||||
elif [ "$CMD" == "restart" ]; then
|
||||
stop_server
|
||||
start_server
|
||||
else
|
||||
echo "Unknown option: $1 (Expected start or stop)"
|
||||
fi
|
0
ui/easydiffusion/__init__.py
Normal file
236
ui/easydiffusion/app.py
Normal file
@ -0,0 +1,236 @@
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import json
|
||||
import traceback
|
||||
import logging
|
||||
import shlex
|
||||
from rich.logging import RichHandler
|
||||
|
||||
from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config
|
||||
|
||||
from easydiffusion import task_manager
|
||||
from easydiffusion.utils import log
|
||||
|
||||
# Remove all handlers associated with the root logger object.
|
||||
for handler in logging.root.handlers[:]:
|
||||
logging.root.removeHandler(handler)
|
||||
|
||||
LOG_FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s %(threadName)s %(message)s"
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format=LOG_FORMAT,
|
||||
datefmt="%X",
|
||||
handlers=[RichHandler(markup=True, rich_tracebacks=False, show_time=False, show_level=False)],
|
||||
)
|
||||
|
||||
SD_DIR = os.getcwd()
|
||||
|
||||
SD_UI_DIR = os.getenv("SD_UI_PATH", None)
|
||||
|
||||
CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "..", "scripts"))
|
||||
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "models"))
|
||||
|
||||
USER_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "plugins"))
|
||||
CORE_PLUGINS_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "plugins"))
|
||||
|
||||
USER_UI_PLUGINS_DIR = os.path.join(USER_PLUGINS_DIR, "ui")
|
||||
CORE_UI_PLUGINS_DIR = os.path.join(CORE_PLUGINS_DIR, "ui")
|
||||
USER_SERVER_PLUGINS_DIR = os.path.join(USER_PLUGINS_DIR, "server")
|
||||
UI_PLUGINS_SOURCES = ((CORE_UI_PLUGINS_DIR, "core"), (USER_UI_PLUGINS_DIR, "user"))
|
||||
|
||||
sys.path.append(os.path.dirname(SD_UI_DIR))
|
||||
sys.path.append(USER_SERVER_PLUGINS_DIR)
|
||||
|
||||
OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder
|
||||
PRESERVE_CONFIG_VARS = ["FORCE_FULL_PRECISION"]
|
||||
TASK_TTL = 15 * 60 # Discard last session's task timeout
|
||||
APP_CONFIG_DEFAULTS = {
|
||||
# auto: selects the cuda device with the most free memory, cuda: use the currently active cuda device.
|
||||
"render_devices": "auto", # valid entries: 'auto', 'cpu' or 'cuda:N' (where N is a GPU index)
|
||||
"update_branch": "main",
|
||||
"ui": {
|
||||
"open_browser_on_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def init():
|
||||
os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True)
|
||||
os.makedirs(USER_SERVER_PLUGINS_DIR, exist_ok=True)
|
||||
|
||||
load_server_plugins()
|
||||
|
||||
update_render_threads()
|
||||
|
||||
|
||||
def getConfig(default_val=APP_CONFIG_DEFAULTS):
|
||||
try:
|
||||
config_json_path = os.path.join(CONFIG_DIR, "config.json")
|
||||
if not os.path.exists(config_json_path):
|
||||
config = default_val
|
||||
else:
|
||||
with open(config_json_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
if "net" not in config:
|
||||
config["net"] = {}
|
||||
if os.getenv("SD_UI_BIND_PORT") is not None:
|
||||
config["net"]["listen_port"] = int(os.getenv("SD_UI_BIND_PORT"))
|
||||
else:
|
||||
config["net"]["listen_port"] = 9000
|
||||
if os.getenv("SD_UI_BIND_IP") is not None:
|
||||
config["net"]["listen_to_network"] = os.getenv("SD_UI_BIND_IP") == "0.0.0.0"
|
||||
else:
|
||||
config["net"]["listen_to_network"] = True
|
||||
return config
|
||||
except Exception as e:
|
||||
log.warn(traceback.format_exc())
|
||||
return default_val
|
||||
|
||||
|
||||
def setConfig(config):
|
||||
try: # config.json
|
||||
config_json_path = os.path.join(CONFIG_DIR, "config.json")
|
||||
with open(config_json_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
try: # config.bat
|
||||
config_bat_path = os.path.join(CONFIG_DIR, "config.bat")
|
||||
config_bat = []
|
||||
|
||||
if "update_branch" in config:
|
||||
config_bat.append(f"@set update_branch={config['update_branch']}")
|
||||
|
||||
config_bat.append(f"@set SD_UI_BIND_PORT={config['net']['listen_port']}")
|
||||
bind_ip = "0.0.0.0" if config["net"]["listen_to_network"] else "127.0.0.1"
|
||||
config_bat.append(f"@set SD_UI_BIND_IP={bind_ip}")
|
||||
|
||||
# Preserve these variables if they are set
|
||||
for var in PRESERVE_CONFIG_VARS:
|
||||
if os.getenv(var) is not None:
|
||||
config_bat.append(f"@set {var}={os.getenv(var)}")
|
||||
|
||||
if len(config_bat) > 0:
|
||||
with open(config_bat_path, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(config_bat))
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
try: # config.sh
|
||||
config_sh_path = os.path.join(CONFIG_DIR, "config.sh")
|
||||
config_sh = ["#!/bin/bash"]
|
||||
|
||||
if "update_branch" in config:
|
||||
config_sh.append(f"export update_branch={config['update_branch']}")
|
||||
|
||||
config_sh.append(f"export SD_UI_BIND_PORT={config['net']['listen_port']}")
|
||||
bind_ip = "0.0.0.0" if config["net"]["listen_to_network"] else "127.0.0.1"
|
||||
config_sh.append(f"export SD_UI_BIND_IP={bind_ip}")
|
||||
|
||||
# Preserve these variables if they are set
|
||||
for var in PRESERVE_CONFIG_VARS:
|
||||
if os.getenv(var) is not None:
|
||||
config_bat.append(f'export {var}="{shlex.quote(os.getenv(var))}"')
|
||||
|
||||
if len(config_sh) > 1:
|
||||
with open(config_sh_path, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(config_sh))
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
|
||||
def save_to_config(ckpt_model_name, vae_model_name, hypernetwork_model_name, vram_usage_level):
|
||||
config = getConfig()
|
||||
if "model" not in config:
|
||||
config["model"] = {}
|
||||
|
||||
config["model"]["stable-diffusion"] = ckpt_model_name
|
||||
config["model"]["vae"] = vae_model_name
|
||||
config["model"]["hypernetwork"] = hypernetwork_model_name
|
||||
|
||||
if vae_model_name is None or vae_model_name == "":
|
||||
del config["model"]["vae"]
|
||||
if hypernetwork_model_name is None or hypernetwork_model_name == "":
|
||||
del config["model"]["hypernetwork"]
|
||||
|
||||
config["vram_usage_level"] = vram_usage_level
|
||||
|
||||
setConfig(config)
|
||||
|
||||
|
||||
def update_render_threads():
|
||||
config = getConfig()
|
||||
render_devices = config.get("render_devices", "auto")
|
||||
active_devices = task_manager.get_devices()["active"].keys()
|
||||
|
||||
log.debug(f"requesting for render_devices: {render_devices}")
|
||||
task_manager.update_render_threads(render_devices, active_devices)
|
||||
|
||||
|
||||
def getUIPlugins():
|
||||
plugins = []
|
||||
|
||||
for plugins_dir, dir_prefix in UI_PLUGINS_SOURCES:
|
||||
for file in os.listdir(plugins_dir):
|
||||
if file.endswith(".plugin.js"):
|
||||
plugins.append(f"/plugins/{dir_prefix}/{file}")
|
||||
|
||||
return plugins
|
||||
|
||||
|
||||
def load_server_plugins():
|
||||
if not os.path.exists(USER_SERVER_PLUGINS_DIR):
|
||||
return
|
||||
|
||||
import importlib
|
||||
|
||||
def load_plugin(file):
|
||||
mod_path = file.replace(".py", "")
|
||||
return importlib.import_module(mod_path)
|
||||
|
||||
def apply_plugin(file, plugin):
|
||||
if hasattr(plugin, "get_cond_and_uncond"):
|
||||
import sdkit.generate.image_generator
|
||||
|
||||
sdkit.generate.image_generator.get_cond_and_uncond = plugin.get_cond_and_uncond
|
||||
log.info(f"Overridden get_cond_and_uncond with the one in the server plugin: {file}")
|
||||
|
||||
for file in os.listdir(USER_SERVER_PLUGINS_DIR):
|
||||
file_path = os.path.join(USER_SERVER_PLUGINS_DIR, file)
|
||||
if (not os.path.isdir(file_path) and not file_path.endswith("_plugin.py")) or (
|
||||
os.path.isdir(file_path) and not file_path.endswith("_plugin")
|
||||
):
|
||||
continue
|
||||
|
||||
try:
|
||||
log.info(f"Loading server plugin: {file}")
|
||||
mod = load_plugin(file)
|
||||
|
||||
log.info(f"Applying server plugin: {file}")
|
||||
apply_plugin(file, mod)
|
||||
except:
|
||||
log.warn(f"Error while loading a server plugin")
|
||||
log.warn(traceback.format_exc())
|
||||
|
||||
|
||||
def getIPConfig():
|
||||
try:
|
||||
ips = socket.gethostbyname_ex(socket.gethostname())
|
||||
ips[2].append(ips[0])
|
||||
return ips[2]
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return []
|
||||
|
||||
|
||||
def open_browser():
|
||||
config = getConfig()
|
||||
ui = config.get("ui", {})
|
||||
net = config.get("net", {"listen_port": 9000})
|
||||
port = net.get("listen_port", 9000)
|
||||
if ui.get("open_browser_on_start", True):
|
||||
import webbrowser
|
||||
|
||||
webbrowser.open(f"http://localhost:{port}")
|
236
ui/easydiffusion/device_manager.py
Normal file
@ -0,0 +1,236 @@
|
||||
import os
|
||||
import torch
|
||||
import traceback
|
||||
import re
|
||||
|
||||
from easydiffusion.utils import log
|
||||
|
||||
"""
|
||||
Set `FORCE_FULL_PRECISION` in the environment variables, or in `config.bat`/`config.sh` to set full precision (i.e. float32).
|
||||
Otherwise the models will load at half-precision (i.e. float16).
|
||||
|
||||
Half-precision is fine most of the time. Full precision is only needed for working around GPU bugs (like NVIDIA 16xx GPUs).
|
||||
"""
|
||||
|
||||
COMPARABLE_GPU_PERCENTILE = (
|
||||
0.65 # if a GPU's free_mem is within this % of the GPU with the most free_mem, it will be picked
|
||||
)
|
||||
|
||||
mem_free_threshold = 0
|
||||
|
||||
|
||||
def get_device_delta(render_devices, active_devices):
|
||||
"""
|
||||
render_devices: 'cpu', or 'auto' or ['cuda:N'...]
|
||||
active_devices: ['cpu', 'cuda:N'...]
|
||||
"""
|
||||
|
||||
if render_devices in ("cpu", "auto"):
|
||||
render_devices = [render_devices]
|
||||
elif render_devices is not None:
|
||||
if isinstance(render_devices, str):
|
||||
render_devices = [render_devices]
|
||||
if isinstance(render_devices, list) and len(render_devices) > 0:
|
||||
render_devices = list(filter(lambda x: x.startswith("cuda:"), render_devices))
|
||||
if len(render_devices) == 0:
|
||||
raise Exception(
|
||||
'Invalid render_devices value in config.json. Valid: {"render_devices": ["cuda:0", "cuda:1"...]}, or {"render_devices": "cpu"} or {"render_devices": "auto"}'
|
||||
)
|
||||
|
||||
render_devices = list(filter(lambda x: is_device_compatible(x), render_devices))
|
||||
if len(render_devices) == 0:
|
||||
raise Exception(
|
||||
"Sorry, none of the render_devices configured in config.json are compatible with Stable Diffusion"
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
'Invalid render_devices value in config.json. Valid: {"render_devices": ["cuda:0", "cuda:1"...]}, or {"render_devices": "cpu"} or {"render_devices": "auto"}'
|
||||
)
|
||||
else:
|
||||
render_devices = ["auto"]
|
||||
|
||||
if "auto" in render_devices:
|
||||
render_devices = auto_pick_devices(active_devices)
|
||||
if "cpu" in render_devices:
|
||||
log.warn("WARNING: Could not find a compatible GPU. Using the CPU, but this will be very slow!")
|
||||
|
||||
active_devices = set(active_devices)
|
||||
render_devices = set(render_devices)
|
||||
|
||||
devices_to_start = render_devices - active_devices
|
||||
devices_to_stop = active_devices - render_devices
|
||||
|
||||
return devices_to_start, devices_to_stop
|
||||
|
||||
|
||||
def auto_pick_devices(currently_active_devices):
|
||||
global mem_free_threshold
|
||||
|
||||
if not torch.cuda.is_available():
|
||||
return ["cpu"]
|
||||
|
||||
device_count = torch.cuda.device_count()
|
||||
if device_count == 1:
|
||||
return ["cuda:0"] if is_device_compatible("cuda:0") else ["cpu"]
|
||||
|
||||
log.debug("Autoselecting GPU. Using most free memory.")
|
||||
devices = []
|
||||
for device in range(device_count):
|
||||
device = f"cuda:{device}"
|
||||
if not is_device_compatible(device):
|
||||
continue
|
||||
|
||||
mem_free, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_free /= float(10**9)
|
||||
mem_total /= float(10**9)
|
||||
device_name = torch.cuda.get_device_name(device)
|
||||
log.debug(
|
||||
f"{device} detected: {device_name} - Memory (free/total): {round(mem_free, 2)}Gb / {round(mem_total, 2)}Gb"
|
||||
)
|
||||
devices.append({"device": device, "device_name": device_name, "mem_free": mem_free})
|
||||
|
||||
devices.sort(key=lambda x: x["mem_free"], reverse=True)
|
||||
max_mem_free = devices[0]["mem_free"]
|
||||
curr_mem_free_threshold = COMPARABLE_GPU_PERCENTILE * max_mem_free
|
||||
mem_free_threshold = max(curr_mem_free_threshold, mem_free_threshold)
|
||||
|
||||
# Auto-pick algorithm:
|
||||
# 1. Pick the top 75 percentile of the GPUs, sorted by free_mem.
|
||||
# 2. Also include already-running devices (GPU-only), otherwise their free_mem will
|
||||
# always be very low (since their VRAM contains the model).
|
||||
# These already-running devices probably aren't terrible, since they were picked in the past.
|
||||
# Worst case, the user can restart the program and that'll get rid of them.
|
||||
devices = list(
|
||||
filter((lambda x: x["mem_free"] > mem_free_threshold or x["device"] in currently_active_devices), devices)
|
||||
)
|
||||
devices = list(map(lambda x: x["device"], devices))
|
||||
return devices
|
||||
|
||||
|
||||
def device_init(context, device):
|
||||
"""
|
||||
This function assumes the 'device' has already been verified to be compatible.
|
||||
`get_device_delta()` has already filtered out incompatible devices.
|
||||
"""
|
||||
|
||||
validate_device_id(device, log_prefix="device_init")
|
||||
|
||||
if device == "cpu":
|
||||
context.device = "cpu"
|
||||
context.device_name = get_processor_name()
|
||||
context.half_precision = False
|
||||
log.debug(f"Render device CPU available as {context.device_name}")
|
||||
return
|
||||
|
||||
context.device_name = torch.cuda.get_device_name(device)
|
||||
context.device = device
|
||||
|
||||
# Force full precision on 1660 and 1650 NVIDIA cards to avoid creating green images
|
||||
if needs_to_force_full_precision(context):
|
||||
log.warn(f"forcing full precision on this GPU, to avoid green images. GPU detected: {context.device_name}")
|
||||
# Apply force_full_precision now before models are loaded.
|
||||
context.half_precision = False
|
||||
|
||||
log.info(f'Setting {device} as active, with precision: {"half" if context.half_precision else "full"}')
|
||||
torch.cuda.device(device)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def needs_to_force_full_precision(context):
|
||||
if "FORCE_FULL_PRECISION" in os.environ:
|
||||
return True
|
||||
|
||||
device_name = context.device_name.lower()
|
||||
return (
|
||||
("nvidia" in device_name or "geforce" in device_name or "quadro" in device_name)
|
||||
and (
|
||||
" 1660" in device_name
|
||||
or " 1650" in device_name
|
||||
or " t400" in device_name
|
||||
or " t550" in device_name
|
||||
or " t600" in device_name
|
||||
or " t1000" in device_name
|
||||
or " t1200" in device_name
|
||||
or " t2000" in device_name
|
||||
)
|
||||
) or ("tesla k40m" in device_name)
|
||||
|
||||
|
||||
def get_max_vram_usage_level(device):
|
||||
if device != "cpu":
|
||||
_, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_total /= float(10**9)
|
||||
|
||||
if mem_total < 4.5:
|
||||
return "low"
|
||||
elif mem_total < 6.5:
|
||||
return "balanced"
|
||||
|
||||
return "high"
|
||||
|
||||
|
||||
def validate_device_id(device, log_prefix=""):
|
||||
def is_valid():
|
||||
if not isinstance(device, str):
|
||||
return False
|
||||
if device == "cpu":
|
||||
return True
|
||||
if not device.startswith("cuda:") or not device[5:].isnumeric():
|
||||
return False
|
||||
return True
|
||||
|
||||
if not is_valid():
|
||||
raise EnvironmentError(
|
||||
f"{log_prefix}: device id should be 'cpu', or 'cuda:N' (where N is an integer index for the GPU). Got: {device}"
|
||||
)
|
||||
|
||||
|
||||
def is_device_compatible(device):
|
||||
"""
|
||||
Returns True/False, and prints any compatibility errors
|
||||
"""
|
||||
# static variable "history".
|
||||
is_device_compatible.history = getattr(is_device_compatible, "history", {})
|
||||
try:
|
||||
validate_device_id(device, log_prefix="is_device_compatible")
|
||||
except:
|
||||
log.error(str(e))
|
||||
return False
|
||||
|
||||
if device == "cpu":
|
||||
return True
|
||||
# Memory check
|
||||
try:
|
||||
_, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_total /= float(10**9)
|
||||
if mem_total < 3.0:
|
||||
if is_device_compatible.history.get(device) == None:
|
||||
log.warn(f"GPU {device} with less than 3 GB of VRAM is not compatible with Stable Diffusion")
|
||||
is_device_compatible.history[device] = 1
|
||||
return False
|
||||
except RuntimeError as e:
|
||||
log.error(str(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_processor_name():
|
||||
try:
|
||||
import platform, subprocess
|
||||
|
||||
if platform.system() == "Windows":
|
||||
return platform.processor()
|
||||
elif platform.system() == "Darwin":
|
||||
os.environ["PATH"] = os.environ["PATH"] + os.pathsep + "/usr/sbin"
|
||||
command = "sysctl -n machdep.cpu.brand_string"
|
||||
return subprocess.check_output(command).strip()
|
||||
elif platform.system() == "Linux":
|
||||
command = "cat /proc/cpuinfo"
|
||||
all_info = subprocess.check_output(command, shell=True).decode().strip()
|
||||
for line in all_info.split("\n"):
|
||||
if "model name" in line:
|
||||
return re.sub(".*model name.*:", "", line, 1).strip()
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return "cpu"
|
255
ui/easydiffusion/model_manager.py
Normal file
@ -0,0 +1,255 @@
|
||||
import os
|
||||
|
||||
from easydiffusion import app
|
||||
from easydiffusion.types import TaskData
|
||||
from easydiffusion.utils import log
|
||||
|
||||
from sdkit import Context
|
||||
from sdkit.models import load_model, unload_model, scan_model
|
||||
|
||||
KNOWN_MODEL_TYPES = ["stable-diffusion", "vae", "hypernetwork", "gfpgan", "realesrgan"]
|
||||
MODEL_EXTENSIONS = {
|
||||
"stable-diffusion": [".ckpt", ".safetensors"],
|
||||
"vae": [".vae.pt", ".ckpt", ".safetensors"],
|
||||
"hypernetwork": [".pt", ".safetensors"],
|
||||
"gfpgan": [".pth"],
|
||||
"realesrgan": [".pth"],
|
||||
}
|
||||
DEFAULT_MODELS = {
|
||||
"stable-diffusion": [ # needed to support the legacy installations
|
||||
"custom-model", # only one custom model file was supported initially, creatively named 'custom-model'
|
||||
"sd-v1-4", # Default fallback.
|
||||
],
|
||||
"gfpgan": ["GFPGANv1.3"],
|
||||
"realesrgan": ["RealESRGAN_x4plus"],
|
||||
}
|
||||
MODELS_TO_LOAD_ON_START = ["stable-diffusion", "vae", "hypernetwork"]
|
||||
|
||||
known_models = {}
|
||||
|
||||
|
||||
def init():
|
||||
make_model_folders()
|
||||
getModels() # run this once, to cache the picklescan results
|
||||
|
||||
|
||||
def load_default_models(context: Context):
|
||||
set_vram_optimizations(context)
|
||||
|
||||
# init default model paths
|
||||
for model_type in MODELS_TO_LOAD_ON_START:
|
||||
context.model_paths[model_type] = resolve_model_to_use(model_type=model_type)
|
||||
try:
|
||||
load_model(context, model_type)
|
||||
except Exception as e:
|
||||
log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]")
|
||||
log.error(f"[red]Error: {e}[/red]")
|
||||
log.error(f"[red]Consider removing the model from the model folder.[red]")
|
||||
|
||||
|
||||
def unload_all(context: Context):
|
||||
for model_type in KNOWN_MODEL_TYPES:
|
||||
unload_model(context, model_type)
|
||||
|
||||
|
||||
def resolve_model_to_use(model_name: str = None, model_type: str = None):
|
||||
model_extensions = MODEL_EXTENSIONS.get(model_type, [])
|
||||
default_models = DEFAULT_MODELS.get(model_type, [])
|
||||
config = app.getConfig()
|
||||
|
||||
model_dirs = [os.path.join(app.MODELS_DIR, model_type), app.SD_DIR]
|
||||
if not model_name: # When None try user configured model.
|
||||
# config = getConfig()
|
||||
if "model" in config and model_type in config["model"]:
|
||||
model_name = config["model"][model_type]
|
||||
|
||||
if model_name:
|
||||
# Check models directory
|
||||
models_dir_path = os.path.join(app.MODELS_DIR, model_type, model_name)
|
||||
for model_extension in model_extensions:
|
||||
if os.path.exists(models_dir_path + model_extension):
|
||||
return models_dir_path + model_extension
|
||||
if os.path.exists(model_name + model_extension):
|
||||
return os.path.abspath(model_name + model_extension)
|
||||
|
||||
# Default locations
|
||||
if model_name in default_models:
|
||||
default_model_path = os.path.join(app.SD_DIR, model_name)
|
||||
for model_extension in model_extensions:
|
||||
if os.path.exists(default_model_path + model_extension):
|
||||
return default_model_path + model_extension
|
||||
|
||||
# Can't find requested model, check the default paths.
|
||||
for default_model in default_models:
|
||||
for model_dir in model_dirs:
|
||||
default_model_path = os.path.join(model_dir, default_model)
|
||||
for model_extension in model_extensions:
|
||||
if os.path.exists(default_model_path + model_extension):
|
||||
if model_name is not None:
|
||||
log.warn(
|
||||
f"Could not find the configured custom model {model_name}{model_extension}. Using the default one: {default_model_path}{model_extension}"
|
||||
)
|
||||
return default_model_path + model_extension
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def reload_models_if_necessary(context: Context, task_data: TaskData):
|
||||
model_paths_in_req = {
|
||||
"stable-diffusion": task_data.use_stable_diffusion_model,
|
||||
"vae": task_data.use_vae_model,
|
||||
"hypernetwork": task_data.use_hypernetwork_model,
|
||||
"gfpgan": task_data.use_face_correction,
|
||||
"realesrgan": task_data.use_upscale,
|
||||
"nsfw_checker": True if task_data.block_nsfw else None,
|
||||
}
|
||||
models_to_reload = {
|
||||
model_type: path
|
||||
for model_type, path in model_paths_in_req.items()
|
||||
if context.model_paths.get(model_type) != path
|
||||
}
|
||||
|
||||
if set_vram_optimizations(context): # reload SD
|
||||
models_to_reload["stable-diffusion"] = model_paths_in_req["stable-diffusion"]
|
||||
|
||||
for model_type, model_path_in_req in models_to_reload.items():
|
||||
context.model_paths[model_type] = model_path_in_req
|
||||
|
||||
action_fn = unload_model if context.model_paths[model_type] is None else load_model
|
||||
action_fn(context, model_type, scan_model=False) # we've scanned them already
|
||||
|
||||
|
||||
def resolve_model_paths(task_data: TaskData):
|
||||
task_data.use_stable_diffusion_model = resolve_model_to_use(
|
||||
task_data.use_stable_diffusion_model, model_type="stable-diffusion"
|
||||
)
|
||||
task_data.use_vae_model = resolve_model_to_use(task_data.use_vae_model, model_type="vae")
|
||||
task_data.use_hypernetwork_model = resolve_model_to_use(task_data.use_hypernetwork_model, model_type="hypernetwork")
|
||||
|
||||
if task_data.use_face_correction:
|
||||
task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, "gfpgan")
|
||||
if task_data.use_upscale:
|
||||
task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan")
|
||||
|
||||
|
||||
def set_vram_optimizations(context: Context):
|
||||
config = app.getConfig()
|
||||
vram_usage_level = config.get("vram_usage_level", "balanced")
|
||||
|
||||
if vram_usage_level != context.vram_usage_level:
|
||||
context.vram_usage_level = vram_usage_level
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def make_model_folders():
|
||||
for model_type in KNOWN_MODEL_TYPES:
|
||||
model_dir_path = os.path.join(app.MODELS_DIR, model_type)
|
||||
|
||||
os.makedirs(model_dir_path, exist_ok=True)
|
||||
|
||||
help_file_name = f"Place your {model_type} model files here.txt"
|
||||
help_file_contents = f'Supported extensions: {" or ".join(MODEL_EXTENSIONS.get(model_type))}'
|
||||
|
||||
with open(os.path.join(model_dir_path, help_file_name), "w", encoding="utf-8") as f:
|
||||
f.write(help_file_contents)
|
||||
|
||||
|
||||
def is_malicious_model(file_path):
|
||||
try:
|
||||
if file_path.endswith(".safetensors"):
|
||||
return False
|
||||
scan_result = scan_model(file_path)
|
||||
if scan_result.issues_count > 0 or scan_result.infected_files > 0:
|
||||
log.warn(
|
||||
":warning: [bold red]Scan %s: %d scanned, %d issue, %d infected.[/bold red]"
|
||||
% (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files)
|
||||
)
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
"Scan %s: [green]%d scanned, %d issue, %d infected.[/green]"
|
||||
% (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files)
|
||||
)
|
||||
return False
|
||||
except Exception as e:
|
||||
log.error(f"error while scanning: {file_path}, error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def getModels():
|
||||
models = {
|
||||
"active": {
|
||||
"stable-diffusion": "sd-v1-4",
|
||||
"vae": "",
|
||||
"hypernetwork": "",
|
||||
},
|
||||
"options": {
|
||||
"stable-diffusion": ["sd-v1-4"],
|
||||
"vae": [],
|
||||
"hypernetwork": [],
|
||||
},
|
||||
}
|
||||
|
||||
models_scanned = 0
|
||||
|
||||
class MaliciousModelException(Exception):
|
||||
"Raised when picklescan reports a problem with a model"
|
||||
pass
|
||||
|
||||
def scan_directory(directory, suffixes, directoriesFirst: bool = True):
|
||||
nonlocal models_scanned
|
||||
tree = []
|
||||
for entry in sorted(
|
||||
os.scandir(directory), key=lambda entry: (entry.is_file() == directoriesFirst, entry.name.lower())
|
||||
):
|
||||
if entry.is_file():
|
||||
matching_suffix = list(filter(lambda s: entry.name.endswith(s), suffixes))
|
||||
if len(matching_suffix) == 0:
|
||||
continue
|
||||
matching_suffix = matching_suffix[0]
|
||||
|
||||
mtime = entry.stat().st_mtime
|
||||
mod_time = known_models[entry.path] if entry.path in known_models else -1
|
||||
if mod_time != mtime:
|
||||
models_scanned += 1
|
||||
if is_malicious_model(entry.path):
|
||||
raise MaliciousModelException(entry.path)
|
||||
known_models[entry.path] = mtime
|
||||
tree.append(entry.name[: -len(matching_suffix)])
|
||||
elif entry.is_dir():
|
||||
scan = scan_directory(entry.path, suffixes, directoriesFirst=False)
|
||||
|
||||
if len(scan) != 0:
|
||||
tree.append((entry.name, scan))
|
||||
return tree
|
||||
|
||||
def listModels(model_type):
|
||||
nonlocal models_scanned
|
||||
|
||||
model_extensions = MODEL_EXTENSIONS.get(model_type, [])
|
||||
models_dir = os.path.join(app.MODELS_DIR, model_type)
|
||||
if not os.path.exists(models_dir):
|
||||
os.makedirs(models_dir)
|
||||
|
||||
try:
|
||||
models["options"][model_type] = scan_directory(models_dir, model_extensions)
|
||||
except MaliciousModelException as e:
|
||||
models["scan-error"] = e
|
||||
|
||||
# custom models
|
||||
listModels(model_type="stable-diffusion")
|
||||
listModels(model_type="vae")
|
||||
listModels(model_type="hypernetwork")
|
||||
listModels(model_type="gfpgan")
|
||||
|
||||
if models_scanned > 0:
|
||||
log.info(f"[green]Scanned {models_scanned} models. Nothing infected[/]")
|
||||
|
||||
# legacy
|
||||
custom_weight_path = os.path.join(app.SD_DIR, "custom-model.ckpt")
|
||||
if os.path.exists(custom_weight_path):
|
||||
models["options"]["stable-diffusion"].append("custom-model")
|
||||
|
||||
return models
|
177
ui/easydiffusion/renderer.py
Normal file
@ -0,0 +1,177 @@
|
||||
import queue
|
||||
import time
|
||||
import json
|
||||
import pprint
|
||||
|
||||
from easydiffusion import device_manager
|
||||
from easydiffusion.types import TaskData, Response, Image as ResponseImage, UserInitiatedStop, GenerateImageRequest
|
||||
from easydiffusion.utils import get_printable_request, save_images_to_disk, log
|
||||
|
||||
from sdkit import Context
|
||||
from sdkit.generate import generate_images
|
||||
from sdkit.filter import apply_filters
|
||||
from sdkit.utils import img_to_buffer, img_to_base64_str, latent_samples_to_images, gc
|
||||
|
||||
context = Context() # thread-local
|
||||
"""
|
||||
runtime data (bound locally to this thread), for e.g. device, references to loaded models, optimization flags etc
|
||||
"""
|
||||
|
||||
|
||||
def init(device):
|
||||
"""
|
||||
Initializes the fields that will be bound to this runtime's context, and sets the current torch device
|
||||
"""
|
||||
context.stop_processing = False
|
||||
context.temp_images = {}
|
||||
context.partial_x_samples = None
|
||||
|
||||
device_manager.device_init(context, device)
|
||||
|
||||
|
||||
def make_images(
|
||||
req: GenerateImageRequest, task_data: TaskData, data_queue: queue.Queue, task_temp_images: list, step_callback
|
||||
):
|
||||
context.stop_processing = False
|
||||
print_task_info(req, task_data)
|
||||
|
||||
images, seeds = make_images_internal(req, task_data, data_queue, task_temp_images, step_callback)
|
||||
|
||||
res = Response(req, task_data, images=construct_response(images, seeds, task_data, base_seed=req.seed))
|
||||
res = res.json()
|
||||
data_queue.put(json.dumps(res))
|
||||
log.info("Task completed")
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def print_task_info(req: GenerateImageRequest, task_data: TaskData):
|
||||
req_str = pprint.pformat(get_printable_request(req)).replace("[", "\[")
|
||||
task_str = pprint.pformat(task_data.dict()).replace("[", "\[")
|
||||
log.info(f"request: {req_str}")
|
||||
log.info(f"task data: {task_str}")
|
||||
|
||||
|
||||
def make_images_internal(
|
||||
req: GenerateImageRequest, task_data: TaskData, data_queue: queue.Queue, task_temp_images: list, step_callback
|
||||
):
|
||||
|
||||
images, user_stopped = generate_images_internal(
|
||||
req, task_data, data_queue, task_temp_images, step_callback, task_data.stream_image_progress, task_data.stream_image_progress_interval
|
||||
)
|
||||
filtered_images = filter_images(task_data, images, user_stopped)
|
||||
|
||||
if task_data.save_to_disk_path is not None:
|
||||
save_images_to_disk(images, filtered_images, req, task_data)
|
||||
|
||||
seeds = [*range(req.seed, req.seed + len(images))]
|
||||
if task_data.show_only_filtered_image or filtered_images is images:
|
||||
return filtered_images, seeds
|
||||
else:
|
||||
return images + filtered_images, seeds + seeds
|
||||
|
||||
|
||||
def generate_images_internal(
|
||||
req: GenerateImageRequest,
|
||||
task_data: TaskData,
|
||||
data_queue: queue.Queue,
|
||||
task_temp_images: list,
|
||||
step_callback,
|
||||
stream_image_progress: bool,
|
||||
stream_image_progress_interval: int,
|
||||
):
|
||||
context.temp_images.clear()
|
||||
|
||||
callback = make_step_callback(req, task_data, data_queue, task_temp_images, step_callback, stream_image_progress, stream_image_progress_interval)
|
||||
|
||||
try:
|
||||
if req.init_image is not None:
|
||||
req.sampler_name = "ddim"
|
||||
|
||||
images = generate_images(context, callback=callback, **req.dict())
|
||||
user_stopped = False
|
||||
except UserInitiatedStop:
|
||||
images = []
|
||||
user_stopped = True
|
||||
if context.partial_x_samples is not None:
|
||||
images = latent_samples_to_images(context, context.partial_x_samples)
|
||||
finally:
|
||||
if hasattr(context, "partial_x_samples") and context.partial_x_samples is not None:
|
||||
del context.partial_x_samples
|
||||
context.partial_x_samples = None
|
||||
|
||||
return images, user_stopped
|
||||
|
||||
|
||||
def filter_images(task_data: TaskData, images: list, user_stopped):
|
||||
if user_stopped:
|
||||
return images
|
||||
|
||||
filters_to_apply = []
|
||||
if task_data.block_nsfw:
|
||||
filters_to_apply.append("nsfw_checker")
|
||||
if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower():
|
||||
filters_to_apply.append("gfpgan")
|
||||
if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower():
|
||||
filters_to_apply.append("realesrgan")
|
||||
|
||||
if len(filters_to_apply) == 0:
|
||||
return images
|
||||
|
||||
return apply_filters(context, filters_to_apply, images, scale=task_data.upscale_amount)
|
||||
|
||||
|
||||
def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int):
|
||||
return [
|
||||
ResponseImage(
|
||||
data=img_to_base64_str(img, task_data.output_format, task_data.output_quality),
|
||||
seed=seed,
|
||||
)
|
||||
for img, seed in zip(images, seeds)
|
||||
]
|
||||
|
||||
|
||||
def make_step_callback(
|
||||
req: GenerateImageRequest,
|
||||
task_data: TaskData,
|
||||
data_queue: queue.Queue,
|
||||
task_temp_images: list,
|
||||
step_callback,
|
||||
stream_image_progress: bool,
|
||||
stream_image_progress_interval: int,
|
||||
):
|
||||
n_steps = req.num_inference_steps if req.init_image is None else int(req.num_inference_steps * req.prompt_strength)
|
||||
last_callback_time = -1
|
||||
|
||||
def update_temp_img(x_samples, task_temp_images: list):
|
||||
partial_images = []
|
||||
images = latent_samples_to_images(context, x_samples)
|
||||
for i, img in enumerate(images):
|
||||
buf = img_to_buffer(img, output_format="JPEG")
|
||||
|
||||
context.temp_images[f"{task_data.request_id}/{i}"] = buf
|
||||
task_temp_images[i] = buf
|
||||
partial_images.append({"path": f"/image/tmp/{task_data.request_id}/{i}"})
|
||||
del images
|
||||
return partial_images
|
||||
|
||||
def on_image_step(x_samples, i):
|
||||
nonlocal last_callback_time
|
||||
|
||||
context.partial_x_samples = x_samples
|
||||
step_time = time.time() - last_callback_time if last_callback_time != -1 else -1
|
||||
last_callback_time = time.time()
|
||||
|
||||
progress = {"step": i, "step_time": step_time, "total_steps": n_steps}
|
||||
|
||||
if stream_image_progress and stream_image_progress_interval > 0 and i % stream_image_progress_interval == 0:
|
||||
progress["output"] = update_temp_img(x_samples, task_temp_images)
|
||||
|
||||
data_queue.put(json.dumps(progress))
|
||||
|
||||
step_callback()
|
||||
|
||||
if context.stop_processing:
|
||||
raise UserInitiatedStop("User requested that we stop processing")
|
||||
|
||||
return on_image_step
|
285
ui/easydiffusion/server.py
Normal file
@ -0,0 +1,285 @@
|
||||
"""server.py: FastAPI SD-UI Web Host.
|
||||
Notes:
|
||||
async endpoints always run on the main thread. Without they run on the thread pool.
|
||||
"""
|
||||
import os
|
||||
import traceback
|
||||
import datetime
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.responses import FileResponse, JSONResponse, StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from easydiffusion import app, model_manager, task_manager
|
||||
from easydiffusion.types import TaskData, GenerateImageRequest, MergeRequest
|
||||
from easydiffusion.utils import log
|
||||
|
||||
log.info(f"started in {app.SD_DIR}")
|
||||
log.info(f"started at {datetime.datetime.now():%x %X}")
|
||||
|
||||
server_api = FastAPI()
|
||||
|
||||
NOCACHE_HEADERS = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
|
||||
|
||||
class NoCacheStaticFiles(StaticFiles):
|
||||
def is_not_modified(self, response_headers, request_headers) -> bool:
|
||||
if "content-type" in response_headers and (
|
||||
"javascript" in response_headers["content-type"] or "css" in response_headers["content-type"]
|
||||
):
|
||||
response_headers.update(NOCACHE_HEADERS)
|
||||
return False
|
||||
|
||||
return super().is_not_modified(response_headers, request_headers)
|
||||
|
||||
|
||||
class SetAppConfigRequest(BaseModel):
|
||||
update_branch: str = None
|
||||
render_devices: Union[List[str], List[int], str, int] = None
|
||||
model_vae: str = None
|
||||
ui_open_browser_on_start: bool = None
|
||||
listen_to_network: bool = None
|
||||
listen_port: int = None
|
||||
|
||||
|
||||
def init():
|
||||
server_api.mount("/media", NoCacheStaticFiles(directory=os.path.join(app.SD_UI_DIR, "media")), name="media")
|
||||
|
||||
for plugins_dir, dir_prefix in app.UI_PLUGINS_SOURCES:
|
||||
server_api.mount(
|
||||
f"/plugins/{dir_prefix}", NoCacheStaticFiles(directory=plugins_dir), name=f"plugins-{dir_prefix}"
|
||||
)
|
||||
|
||||
@server_api.post("/app_config")
|
||||
async def set_app_config(req: SetAppConfigRequest):
|
||||
return set_app_config_internal(req)
|
||||
|
||||
@server_api.get("/get/{key:path}")
|
||||
def read_web_data(key: str = None):
|
||||
return read_web_data_internal(key)
|
||||
|
||||
@server_api.get("/ping") # Get server and optionally session status.
|
||||
def ping(session_id: str = None):
|
||||
return ping_internal(session_id)
|
||||
|
||||
@server_api.post("/render")
|
||||
def render(req: dict):
|
||||
return render_internal(req)
|
||||
|
||||
@server_api.post("/model/merge")
|
||||
def model_merge(req: dict):
|
||||
print(req)
|
||||
return model_merge_internal(req)
|
||||
|
||||
@server_api.get("/image/stream/{task_id:int}")
|
||||
def stream(task_id: int):
|
||||
return stream_internal(task_id)
|
||||
|
||||
@server_api.get("/image/stop")
|
||||
def stop(task: int):
|
||||
return stop_internal(task)
|
||||
|
||||
@server_api.get("/image/tmp/{task_id:int}/{img_id:int}")
|
||||
def get_image(task_id: int, img_id: int):
|
||||
return get_image_internal(task_id, img_id)
|
||||
|
||||
@server_api.get("/")
|
||||
def read_root():
|
||||
return FileResponse(os.path.join(app.SD_UI_DIR, "index.html"), headers=NOCACHE_HEADERS)
|
||||
|
||||
@server_api.on_event("shutdown")
|
||||
def shutdown_event(): # Signal render thread to close on shutdown
|
||||
task_manager.current_state_error = SystemExit("Application shutting down.")
|
||||
|
||||
|
||||
# API implementations
|
||||
def set_app_config_internal(req: SetAppConfigRequest):
|
||||
config = app.getConfig()
|
||||
if req.update_branch is not None:
|
||||
config["update_branch"] = req.update_branch
|
||||
if req.render_devices is not None:
|
||||
update_render_devices_in_config(config, req.render_devices)
|
||||
if req.ui_open_browser_on_start is not None:
|
||||
if "ui" not in config:
|
||||
config["ui"] = {}
|
||||
config["ui"]["open_browser_on_start"] = req.ui_open_browser_on_start
|
||||
if req.listen_to_network is not None:
|
||||
if "net" not in config:
|
||||
config["net"] = {}
|
||||
config["net"]["listen_to_network"] = bool(req.listen_to_network)
|
||||
if req.listen_port is not None:
|
||||
if "net" not in config:
|
||||
config["net"] = {}
|
||||
config["net"]["listen_port"] = int(req.listen_port)
|
||||
try:
|
||||
app.setConfig(config)
|
||||
|
||||
if req.render_devices:
|
||||
app.update_render_threads()
|
||||
|
||||
return JSONResponse({"status": "OK"}, headers=NOCACHE_HEADERS)
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
def update_render_devices_in_config(config, render_devices):
|
||||
if render_devices not in ("cpu", "auto") and not render_devices.startswith("cuda:"):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid render device requested: {render_devices}")
|
||||
|
||||
if render_devices.startswith("cuda:"):
|
||||
render_devices = render_devices.split(",")
|
||||
|
||||
config["render_devices"] = render_devices
|
||||
|
||||
|
||||
def read_web_data_internal(key: str = None):
|
||||
if not key: # /get without parameters, stable-diffusion easter egg.
|
||||
raise HTTPException(status_code=418, detail="StableDiffusion is drawing a teapot!") # HTTP418 I'm a teapot
|
||||
elif key == "app_config":
|
||||
return JSONResponse(app.getConfig(), headers=NOCACHE_HEADERS)
|
||||
elif key == "system_info":
|
||||
config = app.getConfig()
|
||||
|
||||
output_dir = config.get("force_save_path", os.path.join(os.path.expanduser("~"), app.OUTPUT_DIRNAME))
|
||||
|
||||
system_info = {
|
||||
"devices": task_manager.get_devices(),
|
||||
"hosts": app.getIPConfig(),
|
||||
"default_output_dir": output_dir,
|
||||
"enforce_output_dir": ("force_save_path" in config),
|
||||
}
|
||||
system_info["devices"]["config"] = config.get("render_devices", "auto")
|
||||
return JSONResponse(system_info, headers=NOCACHE_HEADERS)
|
||||
elif key == "models":
|
||||
return JSONResponse(model_manager.getModels(), headers=NOCACHE_HEADERS)
|
||||
elif key == "modifiers":
|
||||
return FileResponse(os.path.join(app.SD_UI_DIR, "modifiers.json"), headers=NOCACHE_HEADERS)
|
||||
elif key == "ui_plugins":
|
||||
return JSONResponse(app.getUIPlugins(), headers=NOCACHE_HEADERS)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f"Request for unknown {key}") # HTTP404 Not Found
|
||||
|
||||
|
||||
def ping_internal(session_id: str = None):
|
||||
if task_manager.is_alive() <= 0: # Check that render threads are alive.
|
||||
if task_manager.current_state_error:
|
||||
raise HTTPException(status_code=500, detail=str(task_manager.current_state_error))
|
||||
raise HTTPException(status_code=500, detail="Render thread is dead.")
|
||||
if task_manager.current_state_error and not isinstance(task_manager.current_state_error, StopAsyncIteration):
|
||||
raise HTTPException(status_code=500, detail=str(task_manager.current_state_error))
|
||||
# Alive
|
||||
response = {"status": str(task_manager.current_state)}
|
||||
if session_id:
|
||||
session = task_manager.get_cached_session(session_id, update_ttl=True)
|
||||
response["tasks"] = {id(t): t.status for t in session.tasks}
|
||||
response["devices"] = task_manager.get_devices()
|
||||
return JSONResponse(response, headers=NOCACHE_HEADERS)
|
||||
|
||||
|
||||
def render_internal(req: dict):
|
||||
try:
|
||||
# separate out the request data into rendering and task-specific data
|
||||
render_req: GenerateImageRequest = GenerateImageRequest.parse_obj(req)
|
||||
task_data: TaskData = TaskData.parse_obj(req)
|
||||
|
||||
# Overwrite user specified save path
|
||||
config = app.getConfig()
|
||||
if "force_save_path" in config:
|
||||
task_data.save_to_disk_path = config["force_save_path"]
|
||||
|
||||
render_req.init_image_mask = req.get("mask") # hack: will rename this in the HTTP API in a future revision
|
||||
|
||||
app.save_to_config(
|
||||
task_data.use_stable_diffusion_model,
|
||||
task_data.use_vae_model,
|
||||
task_data.use_hypernetwork_model,
|
||||
task_data.vram_usage_level,
|
||||
)
|
||||
|
||||
# enqueue the task
|
||||
new_task = task_manager.render(render_req, task_data)
|
||||
response = {
|
||||
"status": str(task_manager.current_state),
|
||||
"queue": len(task_manager.tasks_queue),
|
||||
"stream": f"/image/stream/{id(new_task)}",
|
||||
"task": id(new_task),
|
||||
}
|
||||
return JSONResponse(response, headers=NOCACHE_HEADERS)
|
||||
except ChildProcessError as e: # Render thread is dead
|
||||
raise HTTPException(status_code=500, detail=f"Rendering thread has died.") # HTTP500 Internal Server Error
|
||||
except ConnectionRefusedError as e: # Unstarted task pending limit reached, deny queueing too many.
|
||||
raise HTTPException(status_code=503, detail=str(e)) # HTTP503 Service Unavailable
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
def model_merge_internal(req: dict):
|
||||
try:
|
||||
from sdkit.train import merge_models
|
||||
from easydiffusion.utils.save_utils import filename_regex
|
||||
|
||||
mergeReq: MergeRequest = MergeRequest.parse_obj(req)
|
||||
|
||||
merge_models(
|
||||
model_manager.resolve_model_to_use(mergeReq.model0, "stable-diffusion"),
|
||||
model_manager.resolve_model_to_use(mergeReq.model1, "stable-diffusion"),
|
||||
mergeReq.ratio,
|
||||
os.path.join(app.MODELS_DIR, "stable-diffusion", filename_regex.sub("_", mergeReq.out_path)),
|
||||
mergeReq.use_fp16,
|
||||
)
|
||||
return JSONResponse({"status": "OK"}, headers=NOCACHE_HEADERS)
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
def stream_internal(task_id: int):
|
||||
# TODO Move to WebSockets ??
|
||||
task = task_manager.get_cached_task(task_id, update_ttl=True)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail=f"Request {task_id} not found.") # HTTP404 NotFound
|
||||
# if (id(task) != task_id): raise HTTPException(status_code=409, detail=f'Wrong task id received. Expected:{id(task)}, Received:{task_id}') # HTTP409 Conflict
|
||||
if task.buffer_queue.empty() and not task.lock.locked():
|
||||
if task.response:
|
||||
# log.info(f'Session {session_id} sending cached response')
|
||||
return JSONResponse(task.response, headers=NOCACHE_HEADERS)
|
||||
raise HTTPException(status_code=425, detail="Too Early, task not started yet.") # HTTP425 Too Early
|
||||
# log.info(f'Session {session_id} opened live render stream {id(task.buffer_queue)}')
|
||||
return StreamingResponse(task.read_buffer_generator(), media_type="application/json")
|
||||
|
||||
|
||||
def stop_internal(task: int):
|
||||
if not task:
|
||||
if (
|
||||
task_manager.current_state == task_manager.ServerStates.Online
|
||||
or task_manager.current_state == task_manager.ServerStates.Unavailable
|
||||
):
|
||||
raise HTTPException(status_code=409, detail="Not currently running any tasks.") # HTTP409 Conflict
|
||||
task_manager.current_state_error = StopAsyncIteration("")
|
||||
return {"OK"}
|
||||
task_id = task
|
||||
task = task_manager.get_cached_task(task_id, update_ttl=False)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail=f"Task {task_id} was not found.") # HTTP404 Not Found
|
||||
if isinstance(task.error, StopAsyncIteration):
|
||||
raise HTTPException(status_code=409, detail=f"Task {task_id} is already stopped.") # HTTP409 Conflict
|
||||
task.error = StopAsyncIteration(f"Task {task_id} stop requested.")
|
||||
return {"OK"}
|
||||
|
||||
|
||||
def get_image_internal(task_id: int, img_id: int):
|
||||
task = task_manager.get_cached_task(task_id, update_ttl=True)
|
||||
if not task:
|
||||
raise HTTPException(status_code=410, detail=f"Task {task_id} could not be found.") # HTTP404 NotFound
|
||||
if not task.temp_images[img_id]:
|
||||
raise HTTPException(status_code=425, detail="Too Early, task data is not available yet.") # HTTP425 Too Early
|
||||
try:
|
||||
img_data = task.temp_images[img_id]
|
||||
img_data.seek(0)
|
||||
return StreamingResponse(img_data, media_type="image/jpeg")
|
||||
except KeyError as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
562
ui/easydiffusion/task_manager.py
Normal file
@ -0,0 +1,562 @@
|
||||
"""task_manager.py: manage tasks dispatching and render threads.
|
||||
Notes:
|
||||
render_threads should be the only hard reference held by the manager to the threads.
|
||||
Use weak_thread_data to store all other data using weak keys.
|
||||
This will allow for garbage collection after the thread dies.
|
||||
"""
|
||||
import json
|
||||
import traceback
|
||||
|
||||
TASK_TTL = 15 * 60 # seconds, Discard last session's task timeout
|
||||
|
||||
import torch
|
||||
import queue, threading, time, weakref
|
||||
from typing import Any, Hashable
|
||||
|
||||
from easydiffusion import device_manager
|
||||
from easydiffusion.types import TaskData, GenerateImageRequest
|
||||
from easydiffusion.utils import log
|
||||
|
||||
from sdkit.utils import gc
|
||||
|
||||
THREAD_NAME_PREFIX = ""
|
||||
ERR_LOCK_FAILED = " failed to acquire lock within timeout."
|
||||
LOCK_TIMEOUT = 15 # Maximum locking time in seconds before failing a task.
|
||||
# It's better to get an exception than a deadlock... ALWAYS use timeout in critical paths.
|
||||
|
||||
DEVICE_START_TIMEOUT = 60 # seconds - Maximum time to wait for a render device to init.
|
||||
|
||||
|
||||
class SymbolClass(type): # Print nicely formatted Symbol names.
|
||||
def __repr__(self):
|
||||
return self.__qualname__
|
||||
|
||||
def __str__(self):
|
||||
return self.__name__
|
||||
|
||||
|
||||
class Symbol(metaclass=SymbolClass):
|
||||
pass
|
||||
|
||||
|
||||
class ServerStates:
|
||||
class Init(Symbol):
|
||||
pass
|
||||
|
||||
class LoadingModel(Symbol):
|
||||
pass
|
||||
|
||||
class Online(Symbol):
|
||||
pass
|
||||
|
||||
class Rendering(Symbol):
|
||||
pass
|
||||
|
||||
class Unavailable(Symbol):
|
||||
pass
|
||||
|
||||
|
||||
class RenderTask: # Task with output queue and completion lock.
|
||||
def __init__(self, req: GenerateImageRequest, task_data: TaskData):
|
||||
task_data.request_id = id(self)
|
||||
self.render_request: GenerateImageRequest = req # Initial Request
|
||||
self.task_data: TaskData = task_data
|
||||
self.response: Any = None # Copy of the last reponse
|
||||
self.render_device = None # Select the task affinity. (Not used to change active devices).
|
||||
self.temp_images: list = [None] * req.num_outputs * (1 if task_data.show_only_filtered_image else 2)
|
||||
self.error: Exception = None
|
||||
self.lock: threading.Lock = threading.Lock() # Locks at task start and unlocks when task is completed
|
||||
self.buffer_queue: queue.Queue = queue.Queue() # Queue of JSON string segments
|
||||
|
||||
async def read_buffer_generator(self):
|
||||
try:
|
||||
while not self.buffer_queue.empty():
|
||||
res = self.buffer_queue.get(block=False)
|
||||
self.buffer_queue.task_done()
|
||||
yield res
|
||||
except queue.Empty as e:
|
||||
yield
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.lock.locked():
|
||||
return "running"
|
||||
if isinstance(self.error, StopAsyncIteration):
|
||||
return "stopped"
|
||||
if self.error:
|
||||
return "error"
|
||||
if not self.buffer_queue.empty():
|
||||
return "buffer"
|
||||
if self.response:
|
||||
return "completed"
|
||||
return "pending"
|
||||
|
||||
@property
|
||||
def is_pending(self):
|
||||
return bool(not self.response and not self.error)
|
||||
|
||||
|
||||
# Temporary cache to allow to query tasks results for a short time after they are completed.
|
||||
class DataCache:
|
||||
def __init__(self):
|
||||
self._base = dict()
|
||||
self._lock: threading.Lock = threading.Lock()
|
||||
|
||||
def _get_ttl_time(self, ttl: int) -> int:
|
||||
return int(time.time()) + ttl
|
||||
|
||||
def _is_expired(self, timestamp: int) -> bool:
|
||||
return int(time.time()) >= timestamp
|
||||
|
||||
def clean(self) -> None:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.clean" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
# Create a list of expired keys to delete
|
||||
to_delete = []
|
||||
for key in self._base:
|
||||
ttl, _ = self._base[key]
|
||||
if self._is_expired(ttl):
|
||||
to_delete.append(key)
|
||||
# Remove Items
|
||||
for key in to_delete:
|
||||
(_, val) = self._base[key]
|
||||
if isinstance(val, RenderTask):
|
||||
log.debug(f"RenderTask {key} expired. Data removed.")
|
||||
elif isinstance(val, SessionState):
|
||||
log.debug(f"Session {key} expired. Data removed.")
|
||||
else:
|
||||
log.debug(f"Key {key} expired. Data removed.")
|
||||
del self._base[key]
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def clear(self) -> None:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.clear" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
self._base.clear()
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def delete(self, key: Hashable) -> bool:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.delete" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
if key not in self._base:
|
||||
return False
|
||||
del self._base[key]
|
||||
return True
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def keep(self, key: Hashable, ttl: int) -> bool:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.keep" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
if key in self._base:
|
||||
_, value = self._base.get(key)
|
||||
self._base[key] = (self._get_ttl_time(ttl), value)
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def put(self, key: Hashable, value: Any, ttl: int) -> bool:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.put" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
self._base[key] = (self._get_ttl_time(ttl), value)
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def tryGet(self, key: Hashable) -> Any:
|
||||
if not self._lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("DataCache.tryGet" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
ttl, value = self._base.get(key, (None, None))
|
||||
if ttl is not None and self._is_expired(ttl):
|
||||
log.debug(f"Session {key} expired. Discarding data.")
|
||||
del self._base[key]
|
||||
return None
|
||||
return value
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
|
||||
manager_lock = threading.RLock()
|
||||
render_threads = []
|
||||
current_state = ServerStates.Init
|
||||
current_state_error: Exception = None
|
||||
tasks_queue = []
|
||||
session_cache = DataCache()
|
||||
task_cache = DataCache()
|
||||
weak_thread_data = weakref.WeakKeyDictionary()
|
||||
idle_event: threading.Event = threading.Event()
|
||||
|
||||
|
||||
class SessionState:
|
||||
def __init__(self, id: str):
|
||||
self._id = id
|
||||
self._tasks_ids = []
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
tasks = []
|
||||
for task_id in self._tasks_ids:
|
||||
task = task_cache.tryGet(task_id)
|
||||
if task:
|
||||
tasks.append(task)
|
||||
return tasks
|
||||
|
||||
def put(self, task, ttl=TASK_TTL):
|
||||
task_id = id(task)
|
||||
self._tasks_ids.append(task_id)
|
||||
if not task_cache.put(task_id, task, ttl):
|
||||
return False
|
||||
while len(self._tasks_ids) > len(render_threads) * 2:
|
||||
self._tasks_ids.pop(0)
|
||||
return True
|
||||
|
||||
|
||||
def thread_get_next_task():
|
||||
from easydiffusion import renderer
|
||||
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
log.warn(f"Render thread on device: {renderer.context.device} failed to acquire manager lock.")
|
||||
return None
|
||||
if len(tasks_queue) <= 0:
|
||||
manager_lock.release()
|
||||
return None
|
||||
task = None
|
||||
try: # Select a render task.
|
||||
for queued_task in tasks_queue:
|
||||
if queued_task.render_device and renderer.context.device != queued_task.render_device:
|
||||
# Is asking for a specific render device.
|
||||
if is_alive(queued_task.render_device) > 0:
|
||||
continue # requested device alive, skip current one.
|
||||
else:
|
||||
# Requested device is not active, return error to UI.
|
||||
queued_task.error = Exception(queued_task.render_device + " is not currently active.")
|
||||
task = queued_task
|
||||
break
|
||||
if not queued_task.render_device and renderer.context.device == "cpu" and is_alive() > 1:
|
||||
# not asking for any specific devices, cpu want to grab task but other render devices are alive.
|
||||
continue # Skip Tasks, don't run on CPU unless there is nothing else or user asked for it.
|
||||
task = queued_task
|
||||
break
|
||||
if task is not None:
|
||||
del tasks_queue[tasks_queue.index(task)]
|
||||
return task
|
||||
finally:
|
||||
manager_lock.release()
|
||||
|
||||
|
||||
def thread_render(device):
|
||||
global current_state, current_state_error
|
||||
|
||||
from easydiffusion import renderer, model_manager
|
||||
|
||||
try:
|
||||
renderer.init(device)
|
||||
|
||||
weak_thread_data[threading.current_thread()] = {
|
||||
"device": renderer.context.device,
|
||||
"device_name": renderer.context.device_name,
|
||||
"alive": True,
|
||||
}
|
||||
|
||||
current_state = ServerStates.LoadingModel
|
||||
model_manager.load_default_models(renderer.context)
|
||||
|
||||
current_state = ServerStates.Online
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
weak_thread_data[threading.current_thread()] = {"error": e, "alive": False}
|
||||
return
|
||||
|
||||
while True:
|
||||
session_cache.clean()
|
||||
task_cache.clean()
|
||||
if not weak_thread_data[threading.current_thread()]["alive"]:
|
||||
log.info(f"Shutting down thread for device {renderer.context.device}")
|
||||
model_manager.unload_all(renderer.context)
|
||||
return
|
||||
if isinstance(current_state_error, SystemExit):
|
||||
current_state = ServerStates.Unavailable
|
||||
return
|
||||
task = thread_get_next_task()
|
||||
if task is None:
|
||||
idle_event.clear()
|
||||
idle_event.wait(timeout=1)
|
||||
continue
|
||||
if task.error is not None:
|
||||
log.error(task.error)
|
||||
task.response = {"status": "failed", "detail": str(task.error)}
|
||||
task.buffer_queue.put(json.dumps(task.response))
|
||||
continue
|
||||
if current_state_error:
|
||||
task.error = current_state_error
|
||||
task.response = {"status": "failed", "detail": str(task.error)}
|
||||
task.buffer_queue.put(json.dumps(task.response))
|
||||
continue
|
||||
log.info(f"Session {task.task_data.session_id} starting task {id(task)} on {renderer.context.device_name}")
|
||||
if not task.lock.acquire(blocking=False):
|
||||
raise Exception("Got locked task from queue.")
|
||||
try:
|
||||
|
||||
def step_callback():
|
||||
global current_state_error
|
||||
|
||||
if (
|
||||
isinstance(current_state_error, SystemExit)
|
||||
or isinstance(current_state_error, StopAsyncIteration)
|
||||
or isinstance(task.error, StopAsyncIteration)
|
||||
):
|
||||
renderer.context.stop_processing = True
|
||||
if isinstance(current_state_error, StopAsyncIteration):
|
||||
task.error = current_state_error
|
||||
current_state_error = None
|
||||
log.info(f"Session {task.task_data.session_id} sent cancel signal for task {id(task)}")
|
||||
|
||||
current_state = ServerStates.LoadingModel
|
||||
model_manager.resolve_model_paths(task.task_data)
|
||||
model_manager.reload_models_if_necessary(renderer.context, task.task_data)
|
||||
|
||||
current_state = ServerStates.Rendering
|
||||
task.response = renderer.make_images(
|
||||
task.render_request, task.task_data, task.buffer_queue, task.temp_images, step_callback
|
||||
)
|
||||
# Before looping back to the generator, mark cache as still alive.
|
||||
task_cache.keep(id(task), TASK_TTL)
|
||||
session_cache.keep(task.task_data.session_id, TASK_TTL)
|
||||
except Exception as e:
|
||||
task.error = str(e)
|
||||
task.response = {"status": "failed", "detail": str(task.error)}
|
||||
task.buffer_queue.put(json.dumps(task.response))
|
||||
log.error(traceback.format_exc())
|
||||
finally:
|
||||
gc(renderer.context)
|
||||
task.lock.release()
|
||||
task_cache.keep(id(task), TASK_TTL)
|
||||
session_cache.keep(task.task_data.session_id, TASK_TTL)
|
||||
if isinstance(task.error, StopAsyncIteration):
|
||||
log.info(f"Session {task.task_data.session_id} task {id(task)} cancelled!")
|
||||
elif task.error is not None:
|
||||
log.info(f"Session {task.task_data.session_id} task {id(task)} failed!")
|
||||
else:
|
||||
log.info(
|
||||
f"Session {task.task_data.session_id} task {id(task)} completed by {renderer.context.device_name}."
|
||||
)
|
||||
current_state = ServerStates.Online
|
||||
|
||||
|
||||
def get_cached_task(task_id: str, update_ttl: bool = False):
|
||||
# By calling keep before tryGet, wont discard if was expired.
|
||||
if update_ttl and not task_cache.keep(task_id, TASK_TTL):
|
||||
# Failed to keep task, already gone.
|
||||
return None
|
||||
return task_cache.tryGet(task_id)
|
||||
|
||||
|
||||
def get_cached_session(session_id: str, update_ttl: bool = False):
|
||||
if update_ttl:
|
||||
session_cache.keep(session_id, TASK_TTL)
|
||||
session = session_cache.tryGet(session_id)
|
||||
if not session:
|
||||
session = SessionState(session_id)
|
||||
session_cache.put(session_id, session, TASK_TTL)
|
||||
return session
|
||||
|
||||
|
||||
def get_devices():
|
||||
devices = {
|
||||
"all": {},
|
||||
"active": {},
|
||||
}
|
||||
|
||||
def get_device_info(device):
|
||||
if device == "cpu":
|
||||
return {"name": device_manager.get_processor_name()}
|
||||
|
||||
mem_free, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_free /= float(10**9)
|
||||
mem_total /= float(10**9)
|
||||
|
||||
return {
|
||||
"name": torch.cuda.get_device_name(device),
|
||||
"mem_free": mem_free,
|
||||
"mem_total": mem_total,
|
||||
"max_vram_usage_level": device_manager.get_max_vram_usage_level(device),
|
||||
}
|
||||
|
||||
# list the compatible devices
|
||||
gpu_count = torch.cuda.device_count()
|
||||
for device in range(gpu_count):
|
||||
device = f"cuda:{device}"
|
||||
if not device_manager.is_device_compatible(device):
|
||||
continue
|
||||
|
||||
devices["all"].update({device: get_device_info(device)})
|
||||
|
||||
devices["all"].update({"cpu": get_device_info("cpu")})
|
||||
|
||||
# list the activated devices
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("get_devices" + ERR_LOCK_FAILED)
|
||||
try:
|
||||
for rthread in render_threads:
|
||||
if not rthread.is_alive():
|
||||
continue
|
||||
weak_data = weak_thread_data.get(rthread)
|
||||
if not weak_data or not "device" in weak_data or not "device_name" in weak_data:
|
||||
continue
|
||||
device = weak_data["device"]
|
||||
devices["active"].update({device: get_device_info(device)})
|
||||
finally:
|
||||
manager_lock.release()
|
||||
|
||||
return devices
|
||||
|
||||
|
||||
def is_alive(device=None):
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("is_alive" + ERR_LOCK_FAILED)
|
||||
nbr_alive = 0
|
||||
try:
|
||||
for rthread in render_threads:
|
||||
if device is not None:
|
||||
weak_data = weak_thread_data.get(rthread)
|
||||
if weak_data is None or not "device" in weak_data or weak_data["device"] is None:
|
||||
continue
|
||||
thread_device = weak_data["device"]
|
||||
if thread_device != device:
|
||||
continue
|
||||
if rthread.is_alive():
|
||||
nbr_alive += 1
|
||||
return nbr_alive
|
||||
finally:
|
||||
manager_lock.release()
|
||||
|
||||
|
||||
def start_render_thread(device):
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("start_render_thread" + ERR_LOCK_FAILED)
|
||||
log.info(f"Start new Rendering Thread on device: {device}")
|
||||
try:
|
||||
rthread = threading.Thread(target=thread_render, kwargs={"device": device})
|
||||
rthread.daemon = True
|
||||
rthread.name = THREAD_NAME_PREFIX + device
|
||||
rthread.start()
|
||||
render_threads.append(rthread)
|
||||
finally:
|
||||
manager_lock.release()
|
||||
timeout = DEVICE_START_TIMEOUT
|
||||
while not rthread.is_alive() or not rthread in weak_thread_data or not "device" in weak_thread_data[rthread]:
|
||||
if rthread in weak_thread_data and "error" in weak_thread_data[rthread]:
|
||||
log.error(f"{rthread}, {device}, error: {weak_thread_data[rthread]['error']}")
|
||||
return False
|
||||
if timeout <= 0:
|
||||
return False
|
||||
timeout -= 1
|
||||
time.sleep(1)
|
||||
return True
|
||||
|
||||
|
||||
def stop_render_thread(device):
|
||||
try:
|
||||
device_manager.validate_device_id(device, log_prefix="stop_render_thread")
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT):
|
||||
raise Exception("stop_render_thread" + ERR_LOCK_FAILED)
|
||||
log.info(f"Stopping Rendering Thread on device: {device}")
|
||||
|
||||
try:
|
||||
thread_to_remove = None
|
||||
for rthread in render_threads:
|
||||
weak_data = weak_thread_data.get(rthread)
|
||||
if weak_data is None or not "device" in weak_data or weak_data["device"] is None:
|
||||
continue
|
||||
thread_device = weak_data["device"]
|
||||
if thread_device == device:
|
||||
weak_data["alive"] = False
|
||||
thread_to_remove = rthread
|
||||
break
|
||||
if thread_to_remove is not None:
|
||||
render_threads.remove(rthread)
|
||||
return True
|
||||
finally:
|
||||
manager_lock.release()
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def update_render_threads(render_devices, active_devices):
|
||||
devices_to_start, devices_to_stop = device_manager.get_device_delta(render_devices, active_devices)
|
||||
log.debug(f"devices_to_start: {devices_to_start}")
|
||||
log.debug(f"devices_to_stop: {devices_to_stop}")
|
||||
|
||||
for device in devices_to_stop:
|
||||
if is_alive(device) <= 0:
|
||||
log.debug(f"{device} is not alive")
|
||||
continue
|
||||
if not stop_render_thread(device):
|
||||
log.warn(f"{device} could not stop render thread")
|
||||
|
||||
for device in devices_to_start:
|
||||
if is_alive(device) >= 1:
|
||||
log.debug(f"{device} already registered.")
|
||||
continue
|
||||
if not start_render_thread(device):
|
||||
log.warn(f"{device} failed to start.")
|
||||
|
||||
if is_alive() <= 0: # No running devices, probably invalid user config.
|
||||
raise EnvironmentError(
|
||||
'ERROR: No active render devices! Please verify the "render_devices" value in config.json'
|
||||
)
|
||||
|
||||
log.debug(f"active devices: {get_devices()['active']}")
|
||||
|
||||
|
||||
def shutdown_event(): # Signal render thread to close on shutdown
|
||||
global current_state_error
|
||||
current_state_error = SystemExit("Application shutting down.")
|
||||
|
||||
|
||||
def render(render_req: GenerateImageRequest, task_data: TaskData):
|
||||
current_thread_count = is_alive()
|
||||
if current_thread_count <= 0: # Render thread is dead
|
||||
raise ChildProcessError("Rendering thread has died.")
|
||||
|
||||
# Alive, check if task in cache
|
||||
session = get_cached_session(task_data.session_id, update_ttl=True)
|
||||
pending_tasks = list(filter(lambda t: t.is_pending, session.tasks))
|
||||
if current_thread_count < len(pending_tasks):
|
||||
raise ConnectionRefusedError(
|
||||
f"Session {task_data.session_id} already has {len(pending_tasks)} pending tasks out of {current_thread_count}."
|
||||
)
|
||||
|
||||
new_task = RenderTask(render_req, task_data)
|
||||
if session.put(new_task, TASK_TTL):
|
||||
# Use twice the normal timeout for adding user requests.
|
||||
# Tries to force session.put to fail before tasks_queue.put would.
|
||||
if manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT * 2):
|
||||
try:
|
||||
tasks_queue.append(new_task)
|
||||
idle_event.set()
|
||||
return new_task
|
||||
finally:
|
||||
manager_lock.release()
|
||||
raise RuntimeError("Failed to add task to cache.")
|
103
ui/easydiffusion/types.py
Normal file
@ -0,0 +1,103 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Any
|
||||
|
||||
|
||||
class GenerateImageRequest(BaseModel):
|
||||
prompt: str = ""
|
||||
negative_prompt: str = ""
|
||||
|
||||
seed: int = 42
|
||||
width: int = 512
|
||||
height: int = 512
|
||||
|
||||
num_outputs: int = 1
|
||||
num_inference_steps: int = 50
|
||||
guidance_scale: float = 7.5
|
||||
|
||||
init_image: Any = None
|
||||
init_image_mask: Any = None
|
||||
prompt_strength: float = 0.8
|
||||
preserve_init_image_color_profile = False
|
||||
|
||||
sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms"
|
||||
hypernetwork_strength: float = 0
|
||||
|
||||
|
||||
class TaskData(BaseModel):
|
||||
request_id: str = None
|
||||
session_id: str = "session"
|
||||
save_to_disk_path: str = None
|
||||
vram_usage_level: str = "balanced" # or "low" or "medium"
|
||||
|
||||
use_face_correction: str = None # or "GFPGANv1.3"
|
||||
use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B"
|
||||
upscale_amount: int = 4 # or 2
|
||||
use_stable_diffusion_model: str = "sd-v1-4"
|
||||
# use_stable_diffusion_config: str = "v1-inference"
|
||||
use_vae_model: str = None
|
||||
use_hypernetwork_model: str = None
|
||||
|
||||
show_only_filtered_image: bool = False
|
||||
block_nsfw: bool = False
|
||||
output_format: str = "jpeg" # or "png" or "webp"
|
||||
output_quality: int = 75
|
||||
metadata_output_format: str = "txt" # or "json"
|
||||
stream_image_progress: bool = False
|
||||
stream_image_progress_interval: int = 5
|
||||
|
||||
|
||||
class MergeRequest(BaseModel):
|
||||
model0: str = None
|
||||
model1: str = None
|
||||
ratio: float = None
|
||||
out_path: str = "mix"
|
||||
use_fp16 = True
|
||||
|
||||
|
||||
class Image:
|
||||
data: str # base64
|
||||
seed: int
|
||||
is_nsfw: bool
|
||||
path_abs: str = None
|
||||
|
||||
def __init__(self, data, seed):
|
||||
self.data = data
|
||||
self.seed = seed
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"data": self.data,
|
||||
"seed": self.seed,
|
||||
"path_abs": self.path_abs,
|
||||
}
|
||||
|
||||
|
||||
class Response:
|
||||
render_request: GenerateImageRequest
|
||||
task_data: TaskData
|
||||
images: list
|
||||
|
||||
def __init__(self, render_request: GenerateImageRequest, task_data: TaskData, images: list):
|
||||
self.render_request = render_request
|
||||
self.task_data = task_data
|
||||
self.images = images
|
||||
|
||||
def json(self):
|
||||
del self.render_request.init_image
|
||||
del self.render_request.init_image_mask
|
||||
|
||||
res = {
|
||||
"status": "succeeded",
|
||||
"render_request": self.render_request.dict(),
|
||||
"task_data": self.task_data.dict(),
|
||||
"output": [],
|
||||
}
|
||||
|
||||
for image in self.images:
|
||||
res["output"].append(image.json())
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class UserInitiatedStop(Exception):
|
||||
pass
|
8
ui/easydiffusion/utils/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("easydiffusion")
|
||||
|
||||
from .save_utils import (
|
||||
save_images_to_disk,
|
||||
get_printable_request,
|
||||
)
|
132
ui/easydiffusion/utils/save_utils.py
Normal file
@ -0,0 +1,132 @@
|
||||
import os
|
||||
import time
|
||||
import base64
|
||||
import re
|
||||
|
||||
from easydiffusion.types import TaskData, GenerateImageRequest
|
||||
|
||||
from sdkit.utils import save_images, save_dicts
|
||||
|
||||
filename_regex = re.compile("[^a-zA-Z0-9._-]")
|
||||
|
||||
# keep in sync with `ui/media/js/dnd.js`
|
||||
TASK_TEXT_MAPPING = {
|
||||
"prompt": "Prompt",
|
||||
"width": "Width",
|
||||
"height": "Height",
|
||||
"seed": "Seed",
|
||||
"num_inference_steps": "Steps",
|
||||
"guidance_scale": "Guidance Scale",
|
||||
"prompt_strength": "Prompt Strength",
|
||||
"use_face_correction": "Use Face Correction",
|
||||
"use_upscale": "Use Upscaling",
|
||||
"upscale_amount": "Upscale By",
|
||||
"sampler_name": "Sampler",
|
||||
"negative_prompt": "Negative Prompt",
|
||||
"use_stable_diffusion_model": "Stable Diffusion model",
|
||||
"use_vae_model": "VAE model",
|
||||
"use_hypernetwork_model": "Hypernetwork model",
|
||||
"hypernetwork_strength": "Hypernetwork Strength",
|
||||
}
|
||||
|
||||
|
||||
def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageRequest, task_data: TaskData):
|
||||
now = time.time()
|
||||
save_dir_path = os.path.join(task_data.save_to_disk_path, filename_regex.sub("_", task_data.session_id))
|
||||
metadata_entries = get_metadata_entries_for_request(req, task_data)
|
||||
make_filename = make_filename_callback(req, now=now)
|
||||
|
||||
if task_data.show_only_filtered_image or filtered_images is images:
|
||||
save_images(
|
||||
filtered_images,
|
||||
save_dir_path,
|
||||
file_name=make_filename,
|
||||
output_format=task_data.output_format,
|
||||
output_quality=task_data.output_quality,
|
||||
)
|
||||
if task_data.metadata_output_format.lower() in ["json", "txt", "embed"]:
|
||||
save_dicts(
|
||||
metadata_entries,
|
||||
save_dir_path,
|
||||
file_name=make_filename,
|
||||
output_format=task_data.metadata_output_format,
|
||||
file_format=task_data.output_format,
|
||||
)
|
||||
else:
|
||||
make_filter_filename = make_filename_callback(req, now=now, suffix="filtered")
|
||||
|
||||
save_images(
|
||||
images,
|
||||
save_dir_path,
|
||||
file_name=make_filename,
|
||||
output_format=task_data.output_format,
|
||||
output_quality=task_data.output_quality,
|
||||
)
|
||||
save_images(
|
||||
filtered_images,
|
||||
save_dir_path,
|
||||
file_name=make_filter_filename,
|
||||
output_format=task_data.output_format,
|
||||
output_quality=task_data.output_quality,
|
||||
)
|
||||
if task_data.metadata_output_format.lower() in ["json", "txt", "embed"]:
|
||||
save_dicts(
|
||||
metadata_entries,
|
||||
save_dir_path,
|
||||
file_name=make_filter_filename,
|
||||
output_format=task_data.metadata_output_format,
|
||||
file_format=task_data.output_format,
|
||||
)
|
||||
|
||||
|
||||
def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData):
|
||||
metadata = get_printable_request(req)
|
||||
metadata.update(
|
||||
{
|
||||
"use_stable_diffusion_model": task_data.use_stable_diffusion_model,
|
||||
"use_vae_model": task_data.use_vae_model,
|
||||
"use_hypernetwork_model": task_data.use_hypernetwork_model,
|
||||
"use_face_correction": task_data.use_face_correction,
|
||||
"use_upscale": task_data.use_upscale,
|
||||
}
|
||||
)
|
||||
if metadata["use_upscale"] is not None:
|
||||
metadata["upscale_amount"] = task_data.upscale_amount
|
||||
if task_data.use_hypernetwork_model is None:
|
||||
del metadata["hypernetwork_strength"]
|
||||
|
||||
# if text, format it in the text format expected by the UI
|
||||
is_txt_format = task_data.metadata_output_format.lower() == "txt"
|
||||
if is_txt_format:
|
||||
metadata = {TASK_TEXT_MAPPING[key]: val for key, val in metadata.items() if key in TASK_TEXT_MAPPING}
|
||||
|
||||
entries = [metadata.copy() for _ in range(req.num_outputs)]
|
||||
for i, entry in enumerate(entries):
|
||||
entry["Seed" if is_txt_format else "seed"] = req.seed + i
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def get_printable_request(req: GenerateImageRequest):
|
||||
metadata = req.dict()
|
||||
del metadata["init_image"]
|
||||
del metadata["init_image_mask"]
|
||||
if req.init_image is None:
|
||||
del metadata["prompt_strength"]
|
||||
return metadata
|
||||
|
||||
|
||||
def make_filename_callback(req: GenerateImageRequest, suffix=None, now=None):
|
||||
if now is None:
|
||||
now = time.time()
|
||||
|
||||
def make_filename(i):
|
||||
img_id = base64.b64encode(int(now + i).to_bytes(8, "big")).decode() # Generate unique ID based on time.
|
||||
img_id = img_id.translate({43: None, 47: None, 61: None})[-8:] # Remove + / = and keep last 8 chars.
|
||||
|
||||
prompt_flattened = filename_regex.sub("_", req.prompt)[:50]
|
||||
name = f"{prompt_flattened}_{img_id}"
|
||||
name = name if suffix is None else f"{name}_{suffix}"
|
||||
return name
|
||||
|
||||
return make_filename
|
@ -0,0 +1,171 @@
|
||||
{
|
||||
"_name_or_path": "clip-vit-large-patch14/",
|
||||
"architectures": [
|
||||
"CLIPModel"
|
||||
],
|
||||
"initializer_factor": 1.0,
|
||||
"logit_scale_init_value": 2.6592,
|
||||
"model_type": "clip",
|
||||
"projection_dim": 768,
|
||||
"text_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": 0,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": 2,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 768,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 3072,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"max_position_embeddings": 77,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_text_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 12,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 12,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": 1,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false,
|
||||
"vocab_size": 49408
|
||||
},
|
||||
"text_config_dict": {
|
||||
"hidden_size": 768,
|
||||
"intermediate_size": 3072,
|
||||
"num_attention_heads": 12,
|
||||
"num_hidden_layers": 12,
|
||||
"projection_dim": 768
|
||||
},
|
||||
"torch_dtype": "float32",
|
||||
"transformers_version": null,
|
||||
"vision_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": null,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": null,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 1024,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"image_size": 224,
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 4096,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_vision_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 16,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 24,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": null,
|
||||
"patch_size": 14,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false
|
||||
},
|
||||
"vision_config_dict": {
|
||||
"hidden_size": 1024,
|
||||
"intermediate_size": 4096,
|
||||
"num_attention_heads": 16,
|
||||
"num_hidden_layers": 24,
|
||||
"patch_size": 14,
|
||||
"projection_dim": 768
|
||||
}
|
||||
}
|
482
ui/index.html
Normal file
@ -0,0 +1,482 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Easy Diffusion</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#673AB6">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="stylesheet" href="/media/css/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" href="/media/css/fonts.css">
|
||||
<link rel="stylesheet" href="/media/css/themes.css">
|
||||
<link rel="stylesheet" href="/media/css/main.css">
|
||||
<link rel="stylesheet" href="/media/css/auto-save.css">
|
||||
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css">
|
||||
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css">
|
||||
<link rel="stylesheet" href="/media/css/image-editor.css">
|
||||
<link rel="stylesheet" href="/media/css/searchable-models.css">
|
||||
<link rel="manifest" href="/media/manifest.webmanifest">
|
||||
<script src="/media/js/jquery-3.6.1.min.js"></script>
|
||||
<script src="/media/js/jquery-confirm.min.js"></script>
|
||||
<script src="/media/js/marked.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="top-nav">
|
||||
<div id="logo">
|
||||
<h1>
|
||||
Easy Diffusion
|
||||
<small>v2.5.22 <span id="updateBranchLabel"></span></small>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="server-status">
|
||||
<div id="server-status-color">●</div>
|
||||
<span id="server-status-msg">Stable Diffusion is starting..</span>
|
||||
</div>
|
||||
<div id="tab-container" class="tab-container">
|
||||
<span id="tab-main" class="tab active">
|
||||
<span><i class="fa fa-image icon"></i> Generate</span>
|
||||
</span>
|
||||
<span id="tab-settings" class="tab">
|
||||
<span><i class="fa fa-gear icon"></i> Settings</span>
|
||||
</span>
|
||||
<span id="tab-about" class="tab">
|
||||
<span><i class="fa fa-comments icon"></i> Help & Community</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-content-wrapper">
|
||||
<div id="tab-content-main" class="tab-content active flex-container">
|
||||
<div id="editor">
|
||||
<div id="editor-inputs">
|
||||
<div id="editor-inputs-prompt" class="row">
|
||||
<label for="prompt"><b>Enter Prompt</b></label> <small>or</small> <button id="promptsFromFileBtn" class="tertiaryButton">Load from a file</button>
|
||||
<textarea id="prompt" class="col-free">a photograph of an astronaut riding a horse</textarea>
|
||||
<input id="prompt_from_file" name="prompt_from_file" type="file" /> <!-- hidden -->
|
||||
<label for="negative_prompt" class="collapsible" id="negative_prompt_handle">
|
||||
Negative Prompt
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-prompts#negative-prompts" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top">Click to learn more about Negative Prompts</span></i></a>
|
||||
<small>(optional)</small>
|
||||
</label>
|
||||
<div class="collapsible-content">
|
||||
<textarea id="negative_prompt" name="negative_prompt" placeholder="list the things to remove from the image (e.g. fog, green)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor-inputs-init-image" class="row">
|
||||
<label for="init_image">Initial Image (img2img) <small>(optional)</small> </label>
|
||||
|
||||
<div id="init_image_preview_container" class="image_preview_container">
|
||||
<div id="init_image_wrapper">
|
||||
<img id="init_image_preview" src="" />
|
||||
<span id="init_image_size_box" class="img_bottom_label"></span>
|
||||
<button class="init_image_clear image_clear_btn"><i class="fa-solid fa-xmark"></i></button>
|
||||
</div>
|
||||
<div id="init_image_buttons">
|
||||
<div class="button">
|
||||
<i class="fa-regular fa-folder-open"></i>
|
||||
Browse
|
||||
<input id="init_image" name="init_image" type="file" />
|
||||
</div>
|
||||
<div id="init_image_button_draw" class="button">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
Draw
|
||||
</div>
|
||||
<div id="inpaint_button_container">
|
||||
<div id="init_image_button_inpaint" class="button">
|
||||
<i class="fa-solid fa-paintbrush"></i>
|
||||
Inpaint
|
||||
</div>
|
||||
<input id="enable_mask" name="enable_mask" type="checkbox">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="apply_color_correction_setting" class="pl-5"><input id="apply_color_correction" name="apply_color_correction" type="checkbox"> <label for="apply_color_correction">Preserve color profile <small>(helps during inpainting)</small></label></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="editor-inputs-tags-container" class="row">
|
||||
<label>Image Modifiers <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">click an Image Modifier to remove it, right-click to temporarily disable it, use Ctrl+Mouse Wheel to adjust its weight</span></i></label>
|
||||
<div id="editor-inputs-tags-list"></div>
|
||||
</div>
|
||||
|
||||
<button id="makeImage" class="primaryButton">Make Image</button>
|
||||
<div id="render-buttons">
|
||||
<button id="stopImage" class="secondaryButton">Stop All</button>
|
||||
<button id="pause"><i class="fa-solid fa-pause"></i> Pause All</button>
|
||||
<button id="resume"><i class="fa-solid fa-play"></i> Resume</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="line-separator"></span>
|
||||
|
||||
<div id="editor-settings" class="settings-box panel-box">
|
||||
<h4 class="collapsible">
|
||||
Image Settings
|
||||
<i id="reset-image-settings" class="fa-solid fa-arrow-rotate-left section-button">
|
||||
<span class="simple-tooltip top-left">
|
||||
Reset Image Settings
|
||||
</span>
|
||||
</i>
|
||||
</h4>
|
||||
<div id="editor-settings-entries" class="collapsible-content">
|
||||
<div><table>
|
||||
<tr><b class="settings-subheader">Image Settings</b></tr>
|
||||
<tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="0" onkeypress="preventNonNumericalInput(event)"> <input id="random_seed" name="random_seed" type="checkbox" checked><label for="random_seed">Random</label></td></tr>
|
||||
<tr class="pl-5"><td><label for="num_outputs_total">Number of Images:</label></td><td><input id="num_outputs_total" name="num_outputs_total" value="1" size="1" onkeypress="preventNonNumericalInput(event)"> <label><small>(total)</small></label> <input id="num_outputs_parallel" name="num_outputs_parallel" value="1" size="1" onkeypress="preventNonNumericalInput(event)"> <label for="num_outputs_parallel"><small>(in parallel)</small></label></td></tr>
|
||||
<tr class="pl-5"><td><label for="stable_diffusion_model">Model:</label></td><td class="model-input">
|
||||
<input id="stable_diffusion_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
|
||||
<button id="reload-models" class="secondaryButton reloadModels"><i class='fa-solid fa-rotate'></i></button>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Models" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about custom models</span></i></a>
|
||||
</td></tr>
|
||||
<!-- <tr id="modelConfigSelection" class="pl-5"><td><label for="model_config">Model Config:</i></label></td><td>
|
||||
<select id="model_config" name="model_config">
|
||||
</select>
|
||||
</td></tr> -->
|
||||
<tr class="pl-5"><td><label for="vae_model">Custom VAE:</i></label></td><td>
|
||||
<input id="vae_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about VAEs</span></i></a>
|
||||
</td></tr>
|
||||
<tr id="samplerSelection" class="pl-5"><td><label for="sampler_name">Sampler:</label></td><td>
|
||||
<select id="sampler_name" name="sampler_name">
|
||||
<option value="plms">PLMS</option>
|
||||
<option value="ddim">DDIM</option>
|
||||
<option value="heun">Heun</option>
|
||||
<option value="euler">Euler</option>
|
||||
<option value="euler_a" selected>Euler Ancestral</option>
|
||||
<option value="dpm2">DPM2</option>
|
||||
<option value="dpm2_a">DPM2 Ancestral</option>
|
||||
<option value="lms">LMS</option>
|
||||
<option value="dpm_solver_stability">DPM Solver (Stability AI)</option>
|
||||
<option value="dpmpp_2s_a">DPM++ 2s Ancestral</option>
|
||||
<option value="dpmpp_2m">DPM++ 2m</option>
|
||||
<option value="dpmpp_sde">DPM++ SDE</option>
|
||||
<option value="dpm_fast">DPM Fast</option>
|
||||
<option value="dpm_adaptive">DPM Adaptive</option>
|
||||
<option value="unipc_snr">UniPC SNR</option>
|
||||
<option value="unipc_tu">UniPC TU</option>
|
||||
<option value="unipc_snr_2">UniPC SNR 2</option>
|
||||
<option value="unipc_tu_2">UniPC TC 2</option>
|
||||
<option value="unipc_tq">UniPC TQ</option>
|
||||
</select>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use#samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about samplers</span></i></a>
|
||||
</td></tr>
|
||||
<tr class="pl-5"><td><label>Image Size: </label></td><td>
|
||||
<select id="width" name="width" value="512">
|
||||
<option value="128">128 (*)</option>
|
||||
<option value="192">192</option>
|
||||
<option value="256">256 (*)</option>
|
||||
<option value="320">320</option>
|
||||
<option value="384">384</option>
|
||||
<option value="448">448</option>
|
||||
<option value="512" selected>512 (*)</option>
|
||||
<option value="576">576</option>
|
||||
<option value="640">640</option>
|
||||
<option value="704">704</option>
|
||||
<option value="768">768 (*)</option>
|
||||
<option value="832">832</option>
|
||||
<option value="896">896</option>
|
||||
<option value="960">960</option>
|
||||
<option value="1024">1024 (*)</option>
|
||||
<option value="1280">1280</option>
|
||||
<option value="1536">1536</option>
|
||||
<option value="1792">1792</option>
|
||||
<option value="2048">2048</option>
|
||||
</select>
|
||||
<label for="width"><small>(width)</small></label>
|
||||
<select id="height" name="height" value="512">
|
||||
<option value="128">128 (*)</option>
|
||||
<option value="192">192</option>
|
||||
<option value="256">256 (*)</option>
|
||||
<option value="320">320</option>
|
||||
<option value="384">384</option>
|
||||
<option value="448">448</option>
|
||||
<option value="512" selected>512 (*)</option>
|
||||
<option value="576">576</option>
|
||||
<option value="640">640</option>
|
||||
<option value="704">704</option>
|
||||
<option value="768">768 (*)</option>
|
||||
<option value="832">832</option>
|
||||
<option value="896">896</option>
|
||||
<option value="960">960</option>
|
||||
<option value="1024">1024 (*)</option>
|
||||
<option value="1280">1280</option>
|
||||
<option value="1536">1536</option>
|
||||
<option value="1792">1792</option>
|
||||
<option value="2048">2048</option>
|
||||
</select>
|
||||
<label for="height"><small>(height)</small></label>
|
||||
</td></tr>
|
||||
<tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" size="4" value="25" onkeypress="preventNonNumericalInput(event)"></td></tr>
|
||||
<tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="11" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
|
||||
<tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td></tr>
|
||||
<tr class="pl-5"><td><label for="hypernetwork_model">Hypernetwork:</i></label></td><td>
|
||||
<input id="hypernetwork_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
|
||||
</td></tr>
|
||||
<tr id="hypernetwork_strength_container" class="pl-5">
|
||||
<td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td>
|
||||
<td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td>
|
||||
</tr>
|
||||
<tr class="pl-5"><td><label for="output_format">Output Format:</label></td><td>
|
||||
<select id="output_format" name="output_format">
|
||||
<option value="jpeg" selected>jpeg</option>
|
||||
<option value="png">png</option>
|
||||
<option value="webp">webp</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr class="pl-5" id="output_quality_row"><td><label for="output_quality">Image Quality:</label></td><td>
|
||||
<input id="output_quality_slider" name="output_quality" class="editor-slider" value="75" type="range" min="10" max="95"> <input id="output_quality" name="output_quality" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)">
|
||||
</td></tr>
|
||||
</table></div>
|
||||
|
||||
<div><ul>
|
||||
<li><b class="settings-subheader">Render Settings</b></li>
|
||||
<li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview <small>(uses more VRAM, slower images)</small></label></li>
|
||||
<li class="pl-5"><input id="use_face_correction" name="use_face_correction" type="checkbox"> <label for="use_face_correction">Fix incorrect faces and eyes</label> <div style="display:inline-block;"><input id="gfpgan_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" /></div></li>
|
||||
<li class="pl-5">
|
||||
<input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Scale up by</label>
|
||||
<select id="upscale_amount" name="upscale_amount">
|
||||
<option value="2">2x</option>
|
||||
<option value="4" selected>4x</option>
|
||||
</select>
|
||||
with
|
||||
<select id="upscale_model" name="upscale_model">
|
||||
<option value="RealESRGAN_x4plus" selected>RealESRGAN_x4plus</option>
|
||||
<option value="RealESRGAN_x4plus_anime_6B">RealESRGAN_x4plus_anime_6B</option>
|
||||
</select>
|
||||
</li>
|
||||
<li class="pl-5"><input id="show_only_filtered_image" name="show_only_filtered_image" type="checkbox" checked> <label for="show_only_filtered_image">Show only the corrected/upscaled image</label></li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor-modifiers" class="panel-box">
|
||||
<h4 class="collapsible">
|
||||
Image Modifiers (art styles, tags etc)
|
||||
<i id="modifier-settings-btn" class="fa-solid fa-gear section-button">
|
||||
<span class="simple-tooltip left">
|
||||
Add Custom Modifiers
|
||||
</span>
|
||||
</i>
|
||||
</h4>
|
||||
<div id="editor-modifiers-entries" class="collapsible-content">
|
||||
<div id="editor-modifiers-entries-toolbar">
|
||||
<label for="preview-image">Image Style:</label>
|
||||
<select id="preview-image" name="preview-image" value="portrait">
|
||||
<option value="portrait" selected="">Face</option>
|
||||
<option value="landscape">Landscape</option>
|
||||
</select>
|
||||
|
||||
<label for="modifier-card-size-slider">Thumbnail Size:</label>
|
||||
<input id="modifier-card-size-slider" name="modifier-card-size-slider" value="0" type="range" min="-3" max="5">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="preview" class="col-free">
|
||||
<div id="initial-text">
|
||||
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<br/><br/>
|
||||
You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section
|
||||
and selecting the desired modifiers.<br/><br/>
|
||||
Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
|
||||
</div>
|
||||
|
||||
<div id="preview-content">
|
||||
<div id="preview-tools">
|
||||
<button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can icon"></i> Clear All</button>
|
||||
<button id="save-all-images" class="tertiaryButton"><i class="fa-solid fa-download icon"></i> Download All Images</button>
|
||||
<div class="display-settings">
|
||||
<button id="auto_scroll_btn" class="tertiaryButton">
|
||||
<i class="fa-solid fa-arrows-up-to-line icon"></i>
|
||||
<input id="auto_scroll" name="auto_scroll" type="checkbox" style="display: none">
|
||||
<span class="simple-tooltip left">
|
||||
Scroll to generated image (<span class="state">OFF</span>)
|
||||
</span>
|
||||
</button>
|
||||
<button class="dropdown tertiaryButton">
|
||||
<i class="fa-solid fa-magnifying-glass-plus icon dropbtn"></i>
|
||||
<span class="simple-tooltip left">
|
||||
Image Size
|
||||
</span>
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<div class="dropdown-item">
|
||||
<input id="thumbnail_size" name="thumbnail_size" class="editor-slider" type="range" value="70" min="5" max="200" oninput="sliderUpdate(event)">
|
||||
<input id="thumbnail_size-input" name="thumbnail_size-input" size="3" value="70" pattern="^[0-9.]+$" onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)"> %
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix" style="clear: both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-content-settings" class="tab-content">
|
||||
<div id="system-settings" class="tab-content-inner">
|
||||
<h1>System Settings</h1>
|
||||
<div class="parameters-table"></div>
|
||||
<br/>
|
||||
<button id="save-system-settings-btn" class="primaryButton">Save</button>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<h3><i class="fa fa-microchip icon"></i> System Info</h3>
|
||||
<div id="system-info">
|
||||
<table>
|
||||
<tr><td><label>Processor:</label></td><td id="system-info-cpu" class="value"></td></tr>
|
||||
<tr><td><label>Compatible Graphics Cards (all):</label></td><td id="system-info-gpus-all" class="value"></td></tr>
|
||||
<tr><td></td><td> </td></tr>
|
||||
<tr><td><label>Used for rendering 🔥:</label></td><td id="system-info-rendering-devices" class="value"></td></tr>
|
||||
<tr><td><label>Server Addresses <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">You can access Stable Diffusion UI from other devices using these addresses</span></i> :</label></td><td id="system-info-server-hosts" class="value"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-content-about" class="tab-content">
|
||||
<div class="tab-content-inner">
|
||||
<div class="float-container">
|
||||
<div class="float-child">
|
||||
<h1>Help</h1>
|
||||
<ul id="help-links">
|
||||
<li><span class="help-section">Using the software</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-To-Use" target="_blank"><i class="fa-solid fa-book fa-fw"></i> How to use</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Overview" target="_blank"><i class="fa-solid fa-list fa-fw"></i> UI Overview</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-Prompts" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> Writing prompts</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Inpainting" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Inpainting</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Run on Multiple GPUs</a>
|
||||
</ul>
|
||||
|
||||
<li><span class="help-section">Installation</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" target="_blank"><i class="fa-solid fa-circle-question fa-fw"></i> Troubleshooting</a>
|
||||
</ul>
|
||||
|
||||
<li><span class="help-section">Downloadable Content</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Models" target="_blank"><i class="fa-solid fa-images fa-fw"></i> Custom Models</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Plugins" target="_blank"><i class="fa-solid fa-puzzle-piece fa-fw"></i> UI Plugins</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder" target="_blank"><i class="fa-solid fa-hand-sparkles fa-fw"></i> VAE Variational Auto Encoder</a>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="float-child">
|
||||
<h1>Community</h1>
|
||||
<ul id="community-links">
|
||||
<li><a href="https://discord.com/invite/u9yhsFmEkB" target="_blank"><i class="fa-brands fa-discord fa-fw"></i> Discord user community</a></li>
|
||||
<li><a href="https://www.reddit.com/r/StableDiffusionUI/" target="_blank"><i class="fa-brands fa-reddit fa-fw"></i> Reddit community</a></li>
|
||||
<li><a href="https://github.com/cmdr2/stable-diffusion-ui" target="_blank"><i class="fa-brands fa-github fa-fw"></i> Source code on GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="save-settings-config" class="popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Save Settings Configuration</h1>
|
||||
<p>Select which settings should be remembered when restarting the browser</p>
|
||||
<table id="save-settings-config-table" class="form-table">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modifier-settings-config" class="popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Modifier Settings</h1>
|
||||
<p>Set your custom modifiers (one per line)</p>
|
||||
<textarea id="custom-modifiers-input" placeholder="Enter your custom modifiers, one-per-line"></textarea>
|
||||
<p><small><b>Tip:</b> You can include special characters like {} () [] and |. You can also put multiple comma-separated phrases in a single line, to make a single modifier that combines all of those.</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="image-editor" class="popup image-editor-popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Image Editor</h1>
|
||||
<div class="flex-container">
|
||||
<div class="editor-controls-left"></div>
|
||||
<div class="editor-controls-center">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="editor-controls-right">
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="image-inpainter" class="popup image-editor-popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Inpainter</h1>
|
||||
<div class="flex-container">
|
||||
<div class="editor-controls-left"></div>
|
||||
<div class="editor-controls-center">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="editor-controls-right">
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer-spacer"></div>
|
||||
<div id="footer">
|
||||
<div class="line-separator"> </div>
|
||||
<p>If you found this project useful and want to help keep it alive, please <a href="https://ko-fi.com/cmdr2_stablediffusion_ui" target="_blank"><img src="/media/images/kofi.png" id="coffeeButton"></a> to help cover the cost of development and maintenance! Thank you for your support!</p>
|
||||
<p>Please feel free to join the <a href="https://discord.com/invite/u9yhsFmEkB" target="_blank">discord community</a> or <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<div id="footer-legal">
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="media/js/utils.js"></script>
|
||||
<script src="media/js/engine.js"></script>
|
||||
<script src="media/js/parameters.js"></script>
|
||||
<script src="media/js/plugins.js"></script>
|
||||
|
||||
<script src="media/js/image-modifiers.js"></script>
|
||||
<script src="media/js/auto-save.js"></script>
|
||||
|
||||
<script src="media/js/searchable-models.js"></script>
|
||||
<script src="media/js/main.js"></script>
|
||||
<script src="media/js/themes.js"></script>
|
||||
<script src="media/js/dnd.js"></script>
|
||||
<script src="media/js/image-editor.js"></script>
|
||||
<script>
|
||||
async function init() {
|
||||
await initSettings()
|
||||
await getModels()
|
||||
await getAppConfig()
|
||||
await loadUIPlugins()
|
||||
await loadModifiers()
|
||||
await getSystemInfo()
|
||||
|
||||
SD.init({
|
||||
events: {
|
||||
statusChange: setServerStatus
|
||||
, idle: onIdle
|
||||
}
|
||||
})
|
||||
|
||||
playSound()
|
||||
}
|
||||
|
||||
init()
|
||||
</script>
|
||||
</html>
|
10
ui/main.py
Normal file
@ -0,0 +1,10 @@
|
||||
from easydiffusion import model_manager, app, server
|
||||
from easydiffusion.server import server_api # required for uvicorn
|
||||
|
||||
# Init the app
|
||||
model_manager.init()
|
||||
app.init()
|
||||
server.init()
|
||||
|
||||
# start the browser ui
|
||||
app.open_browser()
|
81
ui/media/css/auto-save.css
Normal file
@ -0,0 +1,81 @@
|
||||
/* Auto-Settings Styling */
|
||||
#auto_save_settings ~ button {
|
||||
margin: 5px;
|
||||
}
|
||||
#auto_save_settings:not(:checked) ~ button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form-table th {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.form-table td:first-child > *,
|
||||
.form-table th:first-child > * {
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.form-table td:last-child > *,
|
||||
.form-table th:last-child > * {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
.parameters-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.parameters-table > div {
|
||||
background: var(--background-color2);
|
||||
display: flex;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.parameters-table small {
|
||||
color: rgb(153, 153, 153);
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(1) {
|
||||
font-size: 20px;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(2) {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.parameters-table > div:first-child {
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
}
|
||||
|
||||
.parameters-table > div:last-child {
|
||||
border-radius: 0px 0px 12px 12px;
|
||||
}
|
||||
|
||||
.parameters-table .fa-fire {
|
||||
color: #F7630C;
|
||||
}
|
6
ui/media/css/fontawesome-all.min.css
vendored
Normal file
40
ui/media/css/fonts.css
Normal file
@ -0,0 +1,40 @@
|
||||
/* work-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-700 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-800 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-800.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
216
ui/media/css/image-editor.css
Normal file
@ -0,0 +1,216 @@
|
||||
.editor-controls-left {
|
||||
padding-left: 32px;
|
||||
text-align: left;
|
||||
padding-bottom: 20px;
|
||||
max-width: min-content;
|
||||
}
|
||||
|
||||
.editor-options-container {
|
||||
display: flex;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.editor-options-container > * {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.editor-options-container > * > * {
|
||||
position: inherit;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
background: var(--background-color3);
|
||||
cursor: pointer;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
.editor-options-container > * > *:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.editor-options-container > * > *.active {
|
||||
border: 1px solid #3584e4;
|
||||
}
|
||||
|
||||
.image_editor_opacity .editor-options-container > * > *:not(.active) {
|
||||
border: 1px solid var(--background-color3);
|
||||
}
|
||||
|
||||
.image_editor_color .editor-options-container {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.image_editor_color .editor-options-container > * {
|
||||
flex: 20%;
|
||||
}
|
||||
.image_editor_color .editor-options-container > * > * {
|
||||
position: relative;
|
||||
}
|
||||
.image_editor_color .editor-options-container > * > *.active::before {
|
||||
content: "\f00c";
|
||||
display: var(--fa-display,inline-block);
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
line-height: 1;
|
||||
text-rendering: auto;
|
||||
font-family: var(--fa-style-family, "Font Awesome 6 Free");
|
||||
font-weight: var(--fa-style, 900);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) scale(125%);
|
||||
color: black;
|
||||
}
|
||||
.image_editor_color .editor-options-container > *:first-child {
|
||||
flex: 100%;
|
||||
}
|
||||
.image_editor_color .editor-options-container > *:first-child > * {
|
||||
width: 100%;
|
||||
}
|
||||
.image_editor_color .editor-options-container > *:first-child > * > input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.image_editor_color .editor-options-container > *:first-child > * > span {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.image_editor_color .editor-options-container > *:first-child > *.active > span {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.image_editor_tool .editor-options-container {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.image_editor_tool .editor-options-container > * {
|
||||
padding: 2px;
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.editor-controls-center {
|
||||
/* background: var(--background-color2); */
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.editor-controls-center > div {
|
||||
position: relative;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.editor-controls-center canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.editor-controls-right {
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.editor-controls-right > div:last-child {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 200px;
|
||||
gap: 5px;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.image-editor-button {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
background: var(--background-color3);
|
||||
}
|
||||
|
||||
.editor-controls-right .image-editor-button {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#init_image_button_inpaint .input-toggle {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
#init_image_button_inpaint .input-toggle input:not(:checked) ~ label {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.image-editor-popup {
|
||||
--popup-margin: 16px;
|
||||
--popup-padding: 24px;
|
||||
}
|
||||
|
||||
.image-editor-popup > div {
|
||||
margin: var(--popup-margin);
|
||||
padding: var(--popup-padding);
|
||||
min-height: calc(100vh - (2 * var(--popup-margin)));
|
||||
max-width: none;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.image-editor-popup h1 {
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.image-editor-popup > div {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.image-editor-popup h1 {
|
||||
position: relative;
|
||||
transform: none;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.image-editor-popup > div > div {
|
||||
min-height: calc(100vh - (2 * var(--popup-margin)) - (2 * var(--popup-padding)));
|
||||
}
|
||||
|
||||
.inpainter .image_editor_color {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inpainter .editor-canvas-background {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
#init_image_preview_container .button {
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
height: 24px;
|
||||
box-shadow: 2px 2px 1px 1px #00000088;
|
||||
}
|
||||
|
||||
#init_image_preview_container .button:hover {
|
||||
background: var(--background-color4)
|
||||
}
|
||||
|
||||
.image-editor-popup .button {
|
||||
display: flex;
|
||||
}
|
||||
.image-editor-popup h4 {
|
||||
text-align: left;
|
||||
}
|
9
ui/media/css/jquery-confirm.min.css
vendored
Normal file
1262
ui/media/css/main.css
Normal file
223
ui/media/css/modifier-thumbnails.css
Normal file
@ -0,0 +1,223 @@
|
||||
.modifier-card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.1s;
|
||||
border-radius: 7px;
|
||||
margin: 3pt 3pt;
|
||||
float: left;
|
||||
width: 8em;
|
||||
height: 11.5em;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 8em 3.5em;
|
||||
gap: 0px 0px;
|
||||
grid-auto-flow: row;
|
||||
grid-template-areas:
|
||||
"modifier-card-image-container"
|
||||
"modifier-card-container";
|
||||
border: 2px solid rgba(255, 255, 255, .05);
|
||||
cursor: pointer;
|
||||
}
|
||||
.modifier-card-size_5 {
|
||||
width: 18em;
|
||||
grid-template-rows: 18em 3.5em;
|
||||
height: 21.5em;
|
||||
}
|
||||
.modifier-card-size_5 .modifier-card-image-overlay {
|
||||
font-size: 8em;
|
||||
}
|
||||
.modifier-card-size_4 {
|
||||
width: 14em;
|
||||
grid-template-rows: 14em 3.5em;
|
||||
height: 17.5em;
|
||||
}
|
||||
.modifier-card-size_4 .modifier-card-image-overlay {
|
||||
font-size: 7em;
|
||||
}
|
||||
.modifier-card-size_3 {
|
||||
width: 11em;
|
||||
grid-template-rows: 11em 3.5em;
|
||||
height: 14.5em;
|
||||
}
|
||||
.modifier-card-size_3 .modifier-card-image-overlay {
|
||||
font-size: 6em;
|
||||
}
|
||||
.modifier-card-size_2 {
|
||||
width: 10em;
|
||||
grid-template-rows: 10em 3.5em;
|
||||
height: 13.5em;
|
||||
}
|
||||
.modifier-card-size_2 .modifier-card-image-overlay {
|
||||
font-size: 6em;
|
||||
}
|
||||
.modifier-card-size_1 {
|
||||
width: 9em;
|
||||
grid-template-rows: 9em 3.5em;
|
||||
height: 12.5em;
|
||||
}
|
||||
.modifier-card-size_1 .modifier-card-image-overlay {
|
||||
font-size: 5em;
|
||||
}
|
||||
.modifier-card-size_-1 {
|
||||
width: 7em;
|
||||
grid-template-rows: 7em 3.5em;
|
||||
height: 10.5em;
|
||||
}
|
||||
.modifier-card-size_-1 .modifier-card-image-overlay {
|
||||
font-size: 4em;
|
||||
}
|
||||
.modifier-card-size_-2 {
|
||||
width: 6em;
|
||||
grid-template-rows: 6em 3.5em;
|
||||
height: 9.5em;
|
||||
}
|
||||
.modifier-card-size_-2 .modifier-card-image-overlay {
|
||||
font-size: 3em;
|
||||
}
|
||||
.modifier-card-size_-3 {
|
||||
width: 5em;
|
||||
grid-template-rows: 5em 3.5em;
|
||||
height: 8.5em;
|
||||
}
|
||||
.modifier-card-size_-3 .modifier-card-image-overlay {
|
||||
font-size: 3em;
|
||||
}
|
||||
.modifier-card-size_-3 .modifier-card-label {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.modifier-card-tiny {
|
||||
width: 6em;
|
||||
height: 9.5em;
|
||||
grid-template-rows: 6em 3.5em;
|
||||
}
|
||||
.modifier-card-tiny .modifier-card-image-overlay {
|
||||
font-size: 4em;
|
||||
}
|
||||
.modifier-card:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 5px 16px 5px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.modifier-card-image-container {
|
||||
border-radius: 5px 5px 0 0;
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
grid-area: modifier-card-image-container;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(255 255 255 / 8%);
|
||||
}
|
||||
.modifier-card-image-container img {
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.modifier-card-image-container * {
|
||||
position: absolute;
|
||||
}
|
||||
.modifier-card-container {
|
||||
text-align: center;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
border-radius: 0 0 5px 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: modifier-card-container;
|
||||
font-weight: 100;
|
||||
font-size: .9em;
|
||||
width: inherit;
|
||||
}
|
||||
.modifier-card-label {
|
||||
padding: 4px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.modifier-card-image-overlay {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
border-radius: 5px 5px 0 0;
|
||||
opacity: 0;
|
||||
font-size: 5em;
|
||||
font-weight: 900;
|
||||
color: rgb(255 255 255 / 50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modifier-card-overlay {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
.modifier-card:hover > .modifier-card-image-container .modifier-card-image-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
.modifier-card:hover > .modifier-card-image-container img {
|
||||
filter: blur(.1em);
|
||||
}
|
||||
.modifier-card:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 5px 16px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#preview-image {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.modifier-card-active {
|
||||
border: 2px solid rgb(179 82 255 / 94%);
|
||||
box-shadow: 0 0px 10px 0 rgb(170 0 229 / 58%);
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.tooltip .tooltip-text {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background: rgb(101,97,181);
|
||||
background: linear-gradient(180deg, rgba(101,97,181,1) 0%, rgba(47,45,85,1) 100%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 105%;
|
||||
left: 39%;
|
||||
margin-left: -60px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
border: 2px solid rgb(90 100 177 / 94%);
|
||||
box-shadow: 0px 10px 20px 5px rgb(11 0 58 / 55%);
|
||||
width: 10em;
|
||||
}
|
||||
.tooltip .tooltip-text::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -0.9em;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgb(90 100 177 / 94%) transparent;
|
||||
}
|
||||
.tooltip:hover .tooltip-text {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
#modifier-card-size-slider {
|
||||
width: 6em;
|
||||
margin-bottom: 0.5em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#modifier-settings-btn {
|
||||
float: right;
|
||||
}
|
||||
#modifier-settings-config textarea {
|
||||
width: 90%;
|
||||
height: 150px;
|
||||
}
|
99
ui/media/css/searchable-models.css
Normal file
@ -0,0 +1,99 @@
|
||||
.model-list {
|
||||
position: absolute;
|
||||
margin-block-start: 2px;
|
||||
display: none;
|
||||
padding-inline-start: 0;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
background: var(--input-background-color);
|
||||
border: var(--input-border-size) solid var(--input-border-color);
|
||||
border-radius: var(--input-border-radius);
|
||||
color: var(--input-text-color);
|
||||
z-index: 1;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.model-list ul {
|
||||
padding-right: 20px;
|
||||
padding-inline-start: 0;
|
||||
margin-top: 3pt;
|
||||
}
|
||||
|
||||
.model-list li {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.model-list .icon {
|
||||
padding-right: 3pt;
|
||||
}
|
||||
|
||||
.model-result {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.model-no-result {
|
||||
color: var(--text-color);
|
||||
list-style: none;
|
||||
padding: 3px 6px 3px 6px;
|
||||
font-size: 9pt;
|
||||
font-style: italic;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.model-list li.model-folder {
|
||||
color: var(--text-color);
|
||||
list-style: none;
|
||||
padding: 6px 6px 6px 6px;
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid var(--background-color1);
|
||||
}
|
||||
|
||||
.model-list li.model-file {
|
||||
color: var(--input-text-color);
|
||||
list-style: none;
|
||||
padding-left: 12px;
|
||||
padding-right:20px;
|
||||
font-size: 10pt;
|
||||
font-weight: normal;
|
||||
transition: none;
|
||||
transition:property: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.model-list li.model-file.in-root-folder {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.model-list li.model-file.selected {
|
||||
background: grey;
|
||||
}
|
||||
|
||||
.model-selector {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.model-selector-arrow {
|
||||
position: absolute;
|
||||
width: 17px;
|
||||
margin: 5px -17px;
|
||||
padding-top: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 8pt;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.model-input {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.reloadModels {
|
||||
background: var(--background-color2);
|
||||
border: none;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
#reload-models.secondaryButton:hover {
|
||||
background: var(--background-color2);
|
||||
}
|
183
ui/media/css/themes.css
Normal file
@ -0,0 +1,183 @@
|
||||
:root {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 4%;
|
||||
--value-base: 13%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.5 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1.5 * var(--value-step))));
|
||||
|
||||
--accent-hue: 267;
|
||||
--accent-lightness: 36%;
|
||||
--accent-lightness-hover: 40%;
|
||||
|
||||
--text-color: #eee;
|
||||
|
||||
--input-text-color: #eee;
|
||||
--input-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.7 * var(--value-step))));
|
||||
--input-border-color: var(--background-color4);
|
||||
|
||||
--button-text-color: var(--input-text-color);
|
||||
--button-color: var(--input-background-color);
|
||||
--button-border: none;
|
||||
|
||||
/* other */
|
||||
--input-border-radius: 4px;
|
||||
--input-border-size: 1px;
|
||||
--accent-color: hsl(var(--accent-hue), 100%, var(--accent-lightness));
|
||||
--accent-color-hover: hsl(var(--accent-hue), 100%, var(--accent-lightness-hover));
|
||||
--accent-text-color: rgb(255, 221, 255);
|
||||
--primary-button-border: none;
|
||||
--input-switch-padding: 1px;
|
||||
--input-height: 18px;
|
||||
--tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (2 * var(--value-step))));
|
||||
--tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (3 * var(--value-step))));
|
||||
--tertiary-color: var(--input-text-color)
|
||||
|
||||
/* Main theme color, hex color fallback. */
|
||||
--theme-color-fallback: #673AB6;
|
||||
}
|
||||
|
||||
.theme-light {
|
||||
--background-color1: white;
|
||||
--background-color2: #ececec;
|
||||
--background-color3: #e7e9eb;
|
||||
--background-color4: #cccccc;
|
||||
|
||||
--text-color: black;
|
||||
|
||||
--input-text-color: black;
|
||||
--input-background-color: #f8f9fa;
|
||||
--input-border-color: grey;
|
||||
|
||||
--theme-color-fallback: #aaaaaa;
|
||||
|
||||
--tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (16.8 * var(--value-step))));
|
||||
--tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (12 * var(--value-step))));
|
||||
|
||||
--accent-text-color: white;
|
||||
}
|
||||
|
||||
.theme-discord {
|
||||
--background-color1: #36393f;
|
||||
--background-color2: #2f3136;
|
||||
--background-color3: #292b2f;
|
||||
--background-color4: #202225;
|
||||
|
||||
--accent-hue: 235;
|
||||
--accent-lightness: 65%;
|
||||
|
||||
--input-border-size: 2px;
|
||||
--input-background-color: #202225;
|
||||
--input-border-color: var(--input-background-color);
|
||||
|
||||
--theme-color-fallback: #202225;
|
||||
|
||||
--tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (3.5 * var(--value-step))));
|
||||
--tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (4.5 * var(--value-step))));
|
||||
--accent-text-color: white;
|
||||
}
|
||||
|
||||
.theme-cool-blue {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 18%;
|
||||
--value-step: 3%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
|
||||
--accent-hue: 212;
|
||||
|
||||
--theme-color-fallback: #0056b8;
|
||||
|
||||
--tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (3.5 * var(--value-step))));
|
||||
--tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (4.5 * var(--value-step))));
|
||||
--accent-text-color: #f7fbff;
|
||||
}
|
||||
|
||||
|
||||
.theme-blurple {
|
||||
--main-hue: 235;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 16%;
|
||||
--value-step: 3%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
|
||||
--theme-color-fallback: #5300b8;
|
||||
|
||||
--tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (3.5 * var(--value-step))));
|
||||
--tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (4.5 * var(--value-step))));
|
||||
}
|
||||
|
||||
.theme-super-dark {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 5%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1.4 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
--input-border-size: 0px;
|
||||
|
||||
--theme-color-fallback: #000000;
|
||||
}
|
||||
|
||||
.theme-wild {
|
||||
--main-hue: 128;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 20%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--accent-hue: 212;
|
||||
|
||||
--input-border-size: 1px;
|
||||
--input-background-color: hsl(222, var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--input-text-color: #FF0000;
|
||||
--input-border-color: #005E05;
|
||||
|
||||
--tertiary-color: white;
|
||||
--accent-text-color: #f7fbff;
|
||||
}
|
||||
|
||||
|
||||
.theme-gnomie {
|
||||
--background-color1: #242424;
|
||||
--background-color2: #353535;
|
||||
--background-color3: #494949;
|
||||
--background-color4: #000000;
|
||||
|
||||
--accent-hue: 213;
|
||||
--accent-lightness: 55%;
|
||||
--accent-color: #2168bf;
|
||||
|
||||
--input-border-radius: 6px;
|
||||
--input-text-color: #ffffff;
|
||||
--input-background-color: #2a2a2a;
|
||||
--input-border-size: 0px;
|
||||
--input-border-color: var(--input-background-color);
|
||||
|
||||
--theme-color-fallback: #2168bf;
|
||||
}
|
||||
|
||||
.theme-gnomie .panel-box {
|
||||
border: none;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 10px;
|
||||
}
|
BIN
ui/media/ding.mp3
Normal file
BIN
ui/media/fonts/fa-brands-400.ttf
Normal file
BIN
ui/media/fonts/fa-brands-400.woff2
Normal file
BIN
ui/media/fonts/fa-regular-400.ttf
Normal file
BIN
ui/media/fonts/fa-regular-400.woff2
Normal file
BIN
ui/media/fonts/fa-solid-900.ttf
Normal file
BIN
ui/media/fonts/fa-solid-900.woff2
Normal file
BIN
ui/media/fonts/fa-v4compatibility.ttf
Normal file
BIN
ui/media/fonts/fa-v4compatibility.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-600.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-600.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-700.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-700.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-800.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-800.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-regular.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-regular.woff2
Normal file
BIN
ui/media/images/fa-eraser.png
Normal file
After Width: | Height: | Size: 11 KiB |
4
ui/media/images/fa-eraser.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 576" width="24" height="24">
|
||||
<!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.-->
|
||||
<path style="filter: drop-shadow(0px 0px 20px white)" d="M290.7 57.4 57.4 290.7c-25 25-25 65.5 0 90.5l80 80c12 12 28.3 18.7 45.3 18.7H512c17.7 0 32-14.3 32-32s-14.3-32-32-32H387.9l130.7-130.6c25-25 25-65.5 0-90.5L381.3 57.4c-25-25-65.5-25-90.5 0zm6.7 358.6H182.6l-80-80 124.7-124.7 137.4 137.4-67.3 67.3z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 571 B |
BIN
ui/media/images/fa-eye-dropper.png
Normal file
After Width: | Height: | Size: 12 KiB |
4
ui/media/images/fa-eye-dropper.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="24" height="24">
|
||||
<!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.-->
|
||||
<path style="filter: drop-shadow(0px 0px 20px white)" d="M341.6 29.2 240.1 130.8l-9.4-9.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-9.4-9.4 101.5-101.6c39-39 39-102.2 0-141.1s-102.2-39-141.1 0zM55.4 323.3c-15 15-23.4 35.4-23.4 56.6v42.4L5.4 462.2c-8.5 12.7-6.8 29.6 4 40.4s27.7 12.5 40.4 4L89.7 480h42.4c21.2 0 41.6-8.4 56.6-23.4l120.7-120.7-45.3-45.3-120.7 120.7c-3 3-7.1 4.7-11.3 4.7H96v-36.1c0-4.2 1.7-8.3 4.7-11.3l120.7-120.7-45.3-45.3L55.4 323.3z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 775 B |
4
ui/media/images/fa-fill.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 576" width="24" height="24">
|
||||
<!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.-->
|
||||
<path style="filter: drop-shadow(0px 0px 20px white)" d="M118.6 9.4c-12.5-12.5-32.7-12.5-45.2 0s-12.5 32.8 0 45.3l81.3 81.3-92.1 92.1c-37.5 37.5-37.5 98.3 0 135.8l117.5 117.5c37.5 37.5 98.3 37.5 135.8 0l190.4-190.5c28.1-28.1 28.1-73.7 0-101.8L354.9 37.7c-28.1-28.1-73.7-28.1-101.8 0l-53.1 53-81.4-81.3zM200 181.3l49.4 49.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L245.3 136l53.1-53.1c3.1-3.1 8.2-3.1 11.3 0l151.4 151.4c3.1 3.1 3.1 8.2 0 11.3L418.7 288H99.5c1.4-5.4 4.2-10.4 8.4-14.6l92.1-92.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 763 B |
BIN
ui/media/images/fa-pencil.png
Normal file
After Width: | Height: | Size: 10 KiB |