Upload sd_portable_downloader.bat
Browse files- sd_portable_downloader.bat +1774 -0
sd_portable_downloader.bat
ADDED
|
@@ -0,0 +1,1774 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
@setlocal DisableDelayedExpansion
|
| 3 |
+
set "__BatFilePath_wjZHNFMz__=%~f0"
|
| 4 |
+
set "__BatFilePathP_ESJYJL3u__=%__BatFilePath_wjZHNFMz__:'=''%"
|
| 5 |
+
set "__WorkPath_XvGO8XXY__=%~dp0"
|
| 6 |
+
set "__POWERSHELL_CODE_ARG_Tbs9uXUY__=%*"
|
| 7 |
+
if "%__WorkPath_XvGO8XXY__:~-1%"=="\" set "__WorkPath_XvGO8XXY__=%__WorkPath_XvGO8XXY__:~0,-1%"
|
| 8 |
+
set "BAT_SCRIPT_ROOT=%__WorkPath_XvGO8XXY__%"
|
| 9 |
+
set "__DefaultPowerShellExecutable_exj9gQWc__="
|
| 10 |
+
if "%__DefaultPowerShellExecutable_exj9gQWc__%" == "" (
|
| 11 |
+
where pwsh >nul 2>&1
|
| 12 |
+
if not errorlevel 1 (
|
| 13 |
+
set "__PowerShellExecutable_yc3YvlUU__=pwsh"
|
| 14 |
+
goto :ExecCode
|
| 15 |
+
)
|
| 16 |
+
where powershell >nul 2>&1
|
| 17 |
+
if not errorlevel 1 (
|
| 18 |
+
set "__PowerShellExecutable_yc3YvlUU__=powershell"
|
| 19 |
+
goto :ExecCode
|
| 20 |
+
)
|
| 21 |
+
) else (
|
| 22 |
+
set "__PowerShellExecutable_yc3YvlUU__=%__DefaultPowerShellExecutable_exj9gQWc__%"
|
| 23 |
+
)
|
| 24 |
+
:ExecCode
|
| 25 |
+
if "%__PowerShellExecutable_yc3YvlUU__%" == "" (
|
| 26 |
+
echo PowerShell Executable Not Found
|
| 27 |
+
pause
|
| 28 |
+
set "_psh_exit_code_=1"
|
| 29 |
+
goto :ExitCode
|
| 30 |
+
)
|
| 31 |
+
cmd /c " "%__PowerShellExecutable_yc3YvlUU__%" -ExecutionPolicy Bypass -nop -c ""$f = [System.IO.File]::ReadAllText('%__BatFilePathP_ESJYJL3u__%') -split ':__PowerShellCodeExec_9cErTct9__\:.*'; try { . ([scriptblock]::Create($f[1])) -BatchPath '%__BatFilePathP_ESJYJL3u__%' } catch { $_; exit 1 } " "
|
| 32 |
+
set "_psh_exit_code_=%ERRORLEVEL%"
|
| 33 |
+
goto :ExitCode
|
| 34 |
+
|
| 35 |
+
:__PowerShellCodeExec_9cErTct9__:
|
| 36 |
+
param (
|
| 37 |
+
[string]$BatchPath,
|
| 38 |
+
[Parameter(ValueFromRemainingArguments=$true)]$ExtraArgs
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
function Get-ExtraArgs-From-Env {
|
| 42 |
+
if ([string]::IsNullOrEmpty($Env:__POWERSHELL_CODE_ARG_Tbs9uXUY__)) {
|
| 43 |
+
return @()
|
| 44 |
+
}
|
| 45 |
+
$launch_args = $Env:__POWERSHELL_CODE_ARG_Tbs9uXUY__
|
| 46 |
+
$arguments = [regex]::Matches($launch_args, '("[^"]*"|''[^'']*''|\S+)') | ForEach-Object {
|
| 47 |
+
$_.Value -replace '^["'']|["'']$', ''
|
| 48 |
+
}
|
| 49 |
+
return $arguments
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
function Get-ExtraArgs {
|
| 53 |
+
param (
|
| 54 |
+
[string[]]$ExcludeArgs = @("-CommandWithArgs")
|
| 55 |
+
)
|
| 56 |
+
$extra_args = New-Object System.Collections.ArrayList
|
| 57 |
+
|
| 58 |
+
foreach ($a in $ExtraArgs) {
|
| 59 |
+
if (!($a -in $ExcludeArgs)) {
|
| 60 |
+
$extra_args.Add($a) | Out-Null
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
if ($extra_args.Count -eq 0) {
|
| 65 |
+
$extra_args = Get-ExtraArgs-From-Env
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
$params = $extra_args.ForEach{
|
| 69 |
+
if ($_ -match '\s|"') { "'{0}'" -f ($_ -replace "'", "''") }
|
| 70 |
+
else { $_ }
|
| 71 |
+
} -join ' '
|
| 72 |
+
|
| 73 |
+
return $params
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
function Get-PowerShell-Code {
|
| 77 |
+
param (
|
| 78 |
+
[string]$ScriptPath,
|
| 79 |
+
[string]$Prefix = "__PowerShellCode_pxwOLOkD__"
|
| 80 |
+
)
|
| 81 |
+
$f = [System.IO.File]::ReadAllText($ScriptPath) -split ":${Prefix}\:.*"
|
| 82 |
+
return $([scriptblock]::Create($f[1]))
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function New-RandomName {
|
| 86 |
+
param(
|
| 87 |
+
[ValidateRange(4, 32)]
|
| 88 |
+
[int]$Length = 32
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
([guid]::NewGuid().ToString("N")).Substring(0, $Length)
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
function New-TemporaryDirectory {
|
| 95 |
+
param(
|
| 96 |
+
[ValidateRange(4, 32)]
|
| 97 |
+
[int]$NameLength = 32
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
do {
|
| 101 |
+
$name = New-RandomName -Length $NameLength
|
| 102 |
+
$path = Join-Path ([IO.Path]::GetTempPath()) $name
|
| 103 |
+
} until (-not (Test-Path $path))
|
| 104 |
+
|
| 105 |
+
New-Item -ItemType Directory -Path $path | Out-Null
|
| 106 |
+
|
| 107 |
+
$obj = [pscustomobject]@{ Path = $path }
|
| 108 |
+
$obj | Add-Member ScriptMethod Dispose {
|
| 109 |
+
if (Test-Path $this.Path) {
|
| 110 |
+
Remove-Item $this.Path -Recurse -Force -ErrorAction SilentlyContinue
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
return $obj
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
function New-TemporaryFile {
|
| 117 |
+
param(
|
| 118 |
+
[string]$Extension = ".tmp",
|
| 119 |
+
[switch]$Open,
|
| 120 |
+
[ValidateRange(4, 32)]
|
| 121 |
+
[int]$NameLength = 32
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
do {
|
| 125 |
+
$name = (New-RandomName -Length $NameLength) + $Extension
|
| 126 |
+
$path = Join-Path ([IO.Path]::GetTempPath()) $name
|
| 127 |
+
} until (-not (Test-Path $path))
|
| 128 |
+
|
| 129 |
+
if ($Open) {
|
| 130 |
+
$fs = [IO.File]::Open(
|
| 131 |
+
$path,
|
| 132 |
+
[IO.FileMode]::CreateNew,
|
| 133 |
+
[IO.FileAccess]::ReadWrite,
|
| 134 |
+
[IO.FileShare]::None
|
| 135 |
+
)
|
| 136 |
+
}
|
| 137 |
+
else {
|
| 138 |
+
New-Item -ItemType File -Path $path | Out-Null
|
| 139 |
+
$fs = $null
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
$obj = [pscustomobject]@{
|
| 143 |
+
Path = $path
|
| 144 |
+
Stream = $fs
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
$obj | Add-Member ScriptMethod Dispose {
|
| 148 |
+
if ($this.Stream) { $this.Stream.Dispose() }
|
| 149 |
+
if (Test-Path $this.Path) {
|
| 150 |
+
Remove-Item $this.Path -Force -ErrorAction SilentlyContinue
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
return $obj
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
function Main {
|
| 158 |
+
$tmp = New-TemporaryDirectory -NameLength 8
|
| 159 |
+
$temp_script_name = "$(New-RandomName -NameLength 8).ps1"
|
| 160 |
+
$temp_script_path = (Join-Path $tmp.Path $temp_script_name)
|
| 161 |
+
$psh_script_encoding = if ($PSVersionTable.PSVersion.Major -le 5) { "UTF8" } else { "utf8BOM" }
|
| 162 |
+
try {
|
| 163 |
+
$psh_code = Get-PowerShell-Code -ScriptPath $BatchPath -Prefix "__PowerShellCode_pxwOLOkD__"
|
| 164 |
+
Set-Content -Value $psh_code -Path $temp_script_path -Encoding $psh_script_encoding -Force
|
| 165 |
+
$extra_args = Get-ExtraArgs
|
| 166 |
+
$Env:__BatFilePath_wjZHNFMz__ = $null
|
| 167 |
+
$Env:__BatFilePathP_ESJYJL3u__ = $null
|
| 168 |
+
$Env:__WorkPath_XvGO8XXY__ = $null
|
| 169 |
+
$Env:__POWERSHELL_CODE_ARG_Tbs9uXUY__ = $null
|
| 170 |
+
$Env:__DefaultPowerShellExecutable_exj9gQWc__ = $null
|
| 171 |
+
$Env:__PowerShellExecutable_yc3YvlUU__ = $null
|
| 172 |
+
Invoke-Expression "& `"$temp_script_path`" $extra_args"
|
| 173 |
+
}
|
| 174 |
+
finally {
|
| 175 |
+
$tmp.Dispose()
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
Main
|
| 180 |
+
:__PowerShellCodeExec_9cErTct9__:
|
| 181 |
+
|
| 182 |
+
:__PowerShellCode_pxwOLOkD__:
|
| 183 |
+
<#
|
| 184 |
+
.SYNOPSIS
|
| 185 |
+
AI整合包可视化下载管理工具
|
| 186 |
+
|
| 187 |
+
.DESCRIPTION
|
| 188 |
+
这是一个功能完整的WPF GUI应用,用于下载和管理各类AI模型应用的可发行包。
|
| 189 |
+
|
| 190 |
+
主要功能:
|
| 191 |
+
- 支持多源下载:HuggingFace、ModelScope
|
| 192 |
+
- 版本选择:稳定版(Stable)、夜间构建版(Nightly)
|
| 193 |
+
- 智能队列管理:支持任务排队、自动调度、中断控制
|
| 194 |
+
- 自动解压:下载完成后可自动提取压缩包
|
| 195 |
+
- 磁盘监控:实时显示目标路径的磁盘空间占用
|
| 196 |
+
- 系统主题适配:自动检测Windows深色/浅色模式
|
| 197 |
+
- 代理配置:自动读取系统代理设置
|
| 198 |
+
- 断点续传:使用Aria2核心支持多线程下载
|
| 199 |
+
|
| 200 |
+
.PARAMETER ScriptRootPath
|
| 201 |
+
脚本执行的根路径。如果未指定,将使用当前工作目录。
|
| 202 |
+
类型: [string]
|
| 203 |
+
示例: "C:\Downloads"
|
| 204 |
+
|
| 205 |
+
.NOTES
|
| 206 |
+
环境要求:
|
| 207 |
+
- Windows 7 (SP1) 或更高版本
|
| 208 |
+
- PowerShell 5.0 或更高版本
|
| 209 |
+
- 需要 .NET Framework 4.5+ (WPF支持)
|
| 210 |
+
- 可选:系统已安装Aria2和7-Zip工具
|
| 211 |
+
|
| 212 |
+
外部依赖:
|
| 213 |
+
- Aria2c.exe: 高性能下载引擎,支持多线程和断点续传
|
| 214 |
+
下载源: https://gitee.com/licyk/sd-webui-all-in-one/raw/master/aria2/windows/amd64/aria2c.exe
|
| 215 |
+
- 7za.exe: 轻量级解压工具,支持7z/zip等多种格式
|
| 216 |
+
下载源: https://modelscope.cn/models/licyks/sd-webui-all-in-one/resolve/master/7za/windows/amd64/7za.exe
|
| 217 |
+
- portable_list.json: 云端资源配置文件
|
| 218 |
+
源地址: https://licyk.github.io/resources/portable_list.json
|
| 219 |
+
|
| 220 |
+
主要类和函数:
|
| 221 |
+
- Get-Aria2-Executable: 获取或下载Aria2可执行文件
|
| 222 |
+
- Get-7za-Executable: 获取或下载7-Zip可执行文件
|
| 223 |
+
- Invoke-DownloadTask: 执行单个下载任务
|
| 224 |
+
- Start-ExtractionTask: 执行解压任务
|
| 225 |
+
- Invoke-Refresh: 从云端刷新可用资源列表
|
| 226 |
+
- Set-Proxy: 配置系统代理
|
| 227 |
+
- Find-Visual-Element: WPF可视树搜索辅助函数
|
| 228 |
+
|
| 229 |
+
.EXAMPLE
|
| 230 |
+
# 标准使用方式
|
| 231 |
+
.\sd_portable_downloader.ps1
|
| 232 |
+
|
| 233 |
+
.EXAMPLE
|
| 234 |
+
# 指定下载保存路径
|
| 235 |
+
.\sd_portable_downloader.ps1 -ScriptRootPath "E:\AIModels"
|
| 236 |
+
|
| 237 |
+
.EXAMPLE
|
| 238 |
+
# 在批处理中调用(cmd.exe)
|
| 239 |
+
powershell -ExecutionPolicy Bypass -File ".\sd_portable_downloader.ps1" -ScriptRootPath "C:\Download"
|
| 240 |
+
|
| 241 |
+
.INPUTS
|
| 242 |
+
[string] ScriptRootPath - 可选的脚本根路径参数
|
| 243 |
+
|
| 244 |
+
.OUTPUTS
|
| 245 |
+
无直接输出。返回GUI窗口交互式界面。
|
| 246 |
+
下载的文件保存到用户指定的目录。
|
| 247 |
+
|
| 248 |
+
.LINK
|
| 249 |
+
项目仓库: https://github.com/licyk/sd-webui-all-in-one
|
| 250 |
+
讨论区: https://github.com/licyk/sd-webui-all-in-one/discussions/1
|
| 251 |
+
|
| 252 |
+
.AUTHOR
|
| 253 |
+
licyk
|
| 254 |
+
|
| 255 |
+
.VERSION
|
| 256 |
+
1.0.2
|
| 257 |
+
|
| 258 |
+
.HISTORY
|
| 259 |
+
|
| 260 |
+
#>
|
| 261 |
+
param (
|
| 262 |
+
[string]$ScriptRootPath
|
| 263 |
+
)
|
| 264 |
+
$script:SD_PORTABLE_DOWNLOADER_VERSION = 105
|
| 265 |
+
Add-Type -AssemblyName PresentationFramework, System.Windows.Forms, System.Drawing
|
| 266 |
+
|
| 267 |
+
# 注入 Win32 API 用于实现毛玻璃效果
|
| 268 |
+
Add-Type -TypeDefinition @"
|
| 269 |
+
using System;
|
| 270 |
+
using System.Runtime.InteropServices;
|
| 271 |
+
|
| 272 |
+
[StructLayout(LayoutKind.Sequential)]
|
| 273 |
+
public struct AccentPolicy {
|
| 274 |
+
public int AccentState;
|
| 275 |
+
public int AccentFlags;
|
| 276 |
+
public int GradientColor;
|
| 277 |
+
public int AnimationId;
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
[StructLayout(LayoutKind.Sequential)]
|
| 281 |
+
public struct WindowCompositionAttributeData {
|
| 282 |
+
public int Attribute;
|
| 283 |
+
public IntPtr Data;
|
| 284 |
+
public int SizeOfData;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
public class BlurHelper {
|
| 288 |
+
[DllImport("user32.dll")]
|
| 289 |
+
public static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
|
| 290 |
+
|
| 291 |
+
[DllImport("dwmapi.dll")]
|
| 292 |
+
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
| 293 |
+
|
| 294 |
+
public static void SetBlurState(IntPtr hwnd, int state) {
|
| 295 |
+
var accent = new AccentPolicy();
|
| 296 |
+
accent.AccentState = state; // 0: Disabled, 3: Blur
|
| 297 |
+
|
| 298 |
+
var accentStructSize = Marshal.SizeOf(accent);
|
| 299 |
+
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
|
| 300 |
+
Marshal.StructureToPtr(accent, accentPtr, false);
|
| 301 |
+
|
| 302 |
+
var data = new WindowCompositionAttributeData();
|
| 303 |
+
data.Attribute = 19; // WCA_ACCENT_POLICY
|
| 304 |
+
data.SizeOfData = accentStructSize;
|
| 305 |
+
data.Data = accentPtr;
|
| 306 |
+
|
| 307 |
+
SetWindowCompositionAttribute(hwnd, ref data);
|
| 308 |
+
Marshal.FreeHGlobal(accentPtr);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
public static void EnableBlur(IntPtr hwnd) {
|
| 312 |
+
SetBlurState(hwnd, 3);
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
public static void SetRounding(IntPtr hwnd, bool enabled) {
|
| 316 |
+
// DWMWA_WINDOW_CORNER_PREFERENCE = 33
|
| 317 |
+
// DWMWCP_ROUND = 2, DWMWCP_DONOTROUND = 1
|
| 318 |
+
int preference = enabled ? 2 : 1;
|
| 319 |
+
DwmSetWindowAttribute(hwnd, 33, ref preference, sizeof(int));
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
public static void SetDarkMode(IntPtr hwnd, bool enabled) {
|
| 323 |
+
// DWMWA_USE_IMMERSIVE_DARK_MODE = 20
|
| 324 |
+
int preference = enabled ? 1 : 0;
|
| 325 |
+
DwmSetWindowAttribute(hwnd, 20, ref preference, sizeof(int));
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
"@ -PassThru | Out-Null
|
| 329 |
+
|
| 330 |
+
function Show-Update-Popup {
|
| 331 |
+
param([string]$LatestVersion, [string]$CurrentVersion, [bool]$IsDarkMode)
|
| 332 |
+
|
| 333 |
+
$github = "https://github.com/licyk/sd-webui-all-in-one/releases/download/portable/sd_portable_downloader.bat"
|
| 334 |
+
$gitee = "https://gitee.com/licyk/sd-webui-all-in-one/releases/download/portable/sd_portable_downloader.bat"
|
| 335 |
+
|
| 336 |
+
# 根据主题定义颜色
|
| 337 |
+
$theme = if ($IsDarkMode) {
|
| 338 |
+
@{ BG = "#2D2D2D"; TextMain = "#FFFFFF"; TextSec = "#AAAAAA"; Border = "#444444"; BtnGray = "#4A4A4A"; BtnGrayText = "#FFFFFF" }
|
| 339 |
+
} else {
|
| 340 |
+
@{ BG = "#FFFFFF"; TextMain = "#333333"; TextSec = "Gray"; Border = "#0078D4"; BtnGray = "#E1E1E1"; BtnGrayText = "#333333" }
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
$rs = [runspacefactory]::CreateRunspace()
|
| 344 |
+
$rs.ApartmentState = "STA"
|
| 345 |
+
$rs.Open()
|
| 346 |
+
$ps = [powershell]::Create().AddScript({
|
| 347 |
+
param($v, $cv, $gh, $gt, $t, $dark)
|
| 348 |
+
Add-Type -AssemblyName PresentationFramework
|
| 349 |
+
|
| 350 |
+
[xml]$xaml = @"
|
| 351 |
+
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
| 352 |
+
Title="更新提示" Height="200" Width="420" WindowStartupLocation="CenterScreen"
|
| 353 |
+
WindowStyle="None" AllowsTransparency="True" Background="Transparent" Topmost="True">
|
| 354 |
+
<Border CornerRadius="12" Background="$($t.BG)" BorderBrush="$($t.Border)" BorderThickness="1.5">
|
| 355 |
+
<Border.Effect>
|
| 356 |
+
<DropShadowEffect BlurRadius="15" ShadowDepth="0" Opacity="0.4"/>
|
| 357 |
+
</Border.Effect>
|
| 358 |
+
<StackPanel Margin="25">
|
| 359 |
+
<TextBlock Text="发现新版本!" FontSize="20" FontWeight="Bold" Foreground="#0078D4" Margin="0,0,0,8"/>
|
| 360 |
+
<TextBlock Text="最新版本: $v (当前版本: $cv)" FontSize="12" Foreground="$($t.TextSec)" Margin="0,0,0,15"/>
|
| 361 |
+
<TextBlock Text="建议下载最新的 .bat 启动器以获得更好的体验。" FontSize="13" Foreground="$($t.TextMain)" Margin="0,0,0,20" TextWrapping="Wrap"/>
|
| 362 |
+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
| 363 |
+
<Button Name="GithubBtn" Content="GitHub 下载" Margin="0,0,10,0" Cursor="Hand" Background="#0078D4" Foreground="White" Padding="12,6" BorderThickness="0">
|
| 364 |
+
<Button.Resources><Style TargetType="Border"><Setter Property="CornerRadius" Value="6"/></Style></Button.Resources>
|
| 365 |
+
</Button>
|
| 366 |
+
<Button Name="GiteeBtn" Content="Gitee 下载" Margin="0,0,10,0" Cursor="Hand" Background="#0078D4" Foreground="White" Padding="12,6" BorderThickness="0">
|
| 367 |
+
<Button.Resources><Style TargetType="Border"><Setter Property="CornerRadius" Value="6"/></Style></Button.Resources>
|
| 368 |
+
</Button>
|
| 369 |
+
<Button Name="CloseBtn" Content="稍后" Cursor="Hand" Background="$($t.BtnGray)" Foreground="$($t.BtnGrayText)" Padding="15,6" BorderThickness="0">
|
| 370 |
+
<Button.Resources><Style TargetType="Border"><Setter Property="CornerRadius" Value="6"/></Style></Button.Resources>
|
| 371 |
+
</Button>
|
| 372 |
+
</StackPanel>
|
| 373 |
+
</StackPanel>
|
| 374 |
+
</Border>
|
| 375 |
+
</Window>
|
| 376 |
+
"@
|
| 377 |
+
$reader = New-Object System.Xml.XmlNodeReader $xaml
|
| 378 |
+
$win = [Windows.Markup.XamlReader]::Load($reader)
|
| 379 |
+
|
| 380 |
+
# 处理 Win32 句柄以设置沉浸式深色模式标题栏(虽然是无边框,但有助于系统集成)
|
| 381 |
+
if ($dark) {
|
| 382 |
+
try {
|
| 383 |
+
(New-Object System.Windows.Interop.WindowInteropHelper($win)).EnsureHandle()
|
| 384 |
+
# 此处由于是子 Runspace,不方便调用主进程定义的 BlurHelper,仅通过 XAML 颜色适配
|
| 385 |
+
} catch {}
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
$win.FindName("GithubBtn").Add_Click({ Start-Process $gh; $win.Close() }.GetNewClosure())
|
| 389 |
+
$win.FindName("GiteeBtn").Add_Click({ Start-Process $gt; $win.Close() }.GetNewClosure())
|
| 390 |
+
$win.FindName("CloseBtn").Add_Click({ $win.Close() }.GetNewClosure())
|
| 391 |
+
$win.Add_MouseLeftButtonDown({ $win.DragMove() }.GetNewClosure())
|
| 392 |
+
|
| 393 |
+
$win.ShowDialog() | Out-Null
|
| 394 |
+
}).AddArgument($LatestVersion).AddArgument($CurrentVersion).AddArgument($github).AddArgument($gitee).AddArgument($theme).AddArgument($IsDarkMode)
|
| 395 |
+
$ps.Runspace = $rs
|
| 396 |
+
$ps.BeginInvoke()
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
function Show-Async-MsgBox {
|
| 400 |
+
param([string]$Message, [string]$Title = "提示", [string]$Icon = "Information")
|
| 401 |
+
|
| 402 |
+
$rs = [runspacefactory]::CreateRunspace()
|
| 403 |
+
$rs.Open()
|
| 404 |
+
$ps = [powershell]::Create().AddScript({
|
| 405 |
+
param($msg, $title, $icon)
|
| 406 |
+
Add-Type -AssemblyName PresentationFramework
|
| 407 |
+
[System.Windows.MessageBox]::Show($msg, $title, "OK", $icon)
|
| 408 |
+
}).AddArgument($Message).AddArgument($Title).AddArgument($Icon)
|
| 409 |
+
$ps.Runspace = $rs
|
| 410 |
+
$ps.BeginInvoke()
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
function Invoke-Update-Async {
|
| 414 |
+
param([bool]$IsDarkMode)
|
| 415 |
+
Write-Host "[更新] 正在后台检查新版本..." -ForegroundColor Cyan
|
| 416 |
+
$urls = @(
|
| 417 |
+
"https://github.com/licyk/sd-webui-all-in-one/raw/main/.github/sd_portable_downloader.ps1",
|
| 418 |
+
"https://gitee.com/licyk/sd-webui-all-in-one/raw/main/.github/sd_portable_downloader.ps1"
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
if ($null -eq $Global:DownloadRunspacePool) {
|
| 422 |
+
$Global:DownloadRunspacePool = [runspacefactory]::CreateRunspacePool(1, 2)
|
| 423 |
+
$Global:DownloadRunspacePool.Open()
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
$ps = [powershell]::Create().AddScript({
|
| 427 |
+
param($urls, $currentVersion)
|
| 428 |
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
| 429 |
+
$errors = @()
|
| 430 |
+
foreach ($url in $urls) {
|
| 431 |
+
try {
|
| 432 |
+
$response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 10 -ErrorAction Stop
|
| 433 |
+
if ($response.Content -match '\$script:SD_PORTABLE_DOWNLOADER_VERSION\s*=\s*(\d+)') {
|
| 434 |
+
$latestVersion = [int]$matches[1]
|
| 435 |
+
if ($latestVersion -gt $currentVersion) {
|
| 436 |
+
return @{ Success = $true; LatestVersion = $latestVersion }
|
| 437 |
+
} else {
|
| 438 |
+
return @{ Success = $false; UpToDate = $true }
|
| 439 |
+
}
|
| 440 |
+
} else {
|
| 441 |
+
$errors += "无法在源中解析到版本号: $url"
|
| 442 |
+
}
|
| 443 |
+
} catch {
|
| 444 |
+
$errors += "访问源失败 ($url): $($_.Exception.Message)"
|
| 445 |
+
}
|
| 446 |
+
}
|
| 447 |
+
return @{ Success = $false; Error = ($errors -join "`n") }
|
| 448 |
+
}).AddArgument($urls).AddArgument($script:SD_PORTABLE_DOWNLOADER_VERSION)
|
| 449 |
+
|
| 450 |
+
$ps.RunspacePool = $Global:DownloadRunspacePool
|
| 451 |
+
$asyncResult = $ps.BeginInvoke()
|
| 452 |
+
$currentVer = $script:SD_PORTABLE_DOWNLOADER_VERSION
|
| 453 |
+
# 显式捕获函数引用,以便 PS5.1 的闭包能够识别
|
| 454 |
+
$ShowPopup = Get-Command Show-Update-Popup
|
| 455 |
+
|
| 456 |
+
$monitorTimer = New-Object System.Windows.Threading.DispatcherTimer
|
| 457 |
+
$monitorTimer.Interval = [TimeSpan]::FromSeconds(1)
|
| 458 |
+
$monitorTimer.Add_Tick({
|
| 459 |
+
if ($asyncResult.IsCompleted) {
|
| 460 |
+
$monitorTimer.Stop()
|
| 461 |
+
try {
|
| 462 |
+
$result = $ps.EndInvoke($asyncResult)
|
| 463 |
+
if ($result.Success) {
|
| 464 |
+
Write-Host "[更新] 发现新版本: $($result.LatestVersion) (当前: $currentVer)" -ForegroundColor Green
|
| 465 |
+
# 使用 & 符号调用捕获的函数变量
|
| 466 |
+
& $ShowPopup -LatestVersion $result.LatestVersion -CurrentVersion $currentVer -IsDarkMode $IsDarkMode
|
| 467 |
+
} elseif ($result.UpToDate) {
|
| 468 |
+
Write-Host "[更新] 当前已是最新版本 ($currentVer)" -ForegroundColor Gray
|
| 469 |
+
} elseif ($result.Error) {
|
| 470 |
+
Write-Host "[更新] 检查更新失败: $($result.Error)" -ForegroundColor Red
|
| 471 |
+
} else {
|
| 472 |
+
Write-Host "[更新] 检查更新任务完成,但未发现有效版本信息。" -ForegroundColor Yellow
|
| 473 |
+
}
|
| 474 |
+
} catch {
|
| 475 |
+
Write-Host "[更新] 检查更新时发生异常: $($_.Exception.Message)" -ForegroundColor Red
|
| 476 |
+
} finally {
|
| 477 |
+
$ps.Dispose()
|
| 478 |
+
}
|
| 479 |
+
}
|
| 480 |
+
}.GetNewClosure())
|
| 481 |
+
$monitorTimer.Start()
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
function Open-Url {
|
| 485 |
+
param([string]$Url)
|
| 486 |
+
try { Start-Process $Url } catch { Write-Warning "无法打开链接: $Url" }
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
function Invoke-WebRequest-Async {
|
| 490 |
+
param([string]$Uri, [string]$OutFile)
|
| 491 |
+
|
| 492 |
+
# 检查当前线程是否有关联的 Dispatcher (UI 线程)
|
| 493 |
+
$dispatcher = [System.Windows.Threading.Dispatcher]::FromThread([System.Threading.Thread]::CurrentThread)
|
| 494 |
+
if ($null -eq $dispatcher) {
|
| 495 |
+
# 非 UI 线程,直接同步执行
|
| 496 |
+
Invoke-WebRequest -UseBasicParsing -Uri $Uri -OutFile $OutFile
|
| 497 |
+
} else {
|
| 498 |
+
# UI 线程,使用后台运行空间下载,并保持 UI 响应
|
| 499 |
+
$rs = [runspacefactory]::CreateRunspace()
|
| 500 |
+
$rs.Open()
|
| 501 |
+
$ps = [powershell]::Create().AddScript({
|
| 502 |
+
param($u, $o)
|
| 503 |
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
| 504 |
+
Invoke-WebRequest -UseBasicParsing -Uri $u -OutFile $o
|
| 505 |
+
}).AddArgument($Uri).AddArgument($OutFile)
|
| 506 |
+
$ps.Runspace = $rs
|
| 507 |
+
$async = $ps.BeginInvoke()
|
| 508 |
+
while (-not $async.IsCompleted) {
|
| 509 |
+
# 允许 UI 处理事件循环
|
| 510 |
+
$dispatcher.Invoke([Action]{}, [System.Windows.Threading.DispatcherPriority]::Background)
|
| 511 |
+
Start-Sleep -Milliseconds 100
|
| 512 |
+
}
|
| 513 |
+
try {
|
| 514 |
+
$ps.EndInvoke($async)
|
| 515 |
+
} finally {
|
| 516 |
+
$ps.Dispose()
|
| 517 |
+
$rs.Close()
|
| 518 |
+
}
|
| 519 |
+
}
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
function Get-Aria2-Executable {
|
| 523 |
+
$binUrl = "https://www.modelscope.cn/models/licyks/sd-webui-all-in-one/resolve/master/aria2/windows/amd64/aria2c.exe"
|
| 524 |
+
$binPath = Join-Path ([IO.Path]::GetTempPath()) "aria2c.exe"
|
| 525 |
+
$localBinPath = Get-Command aria2c -ErrorAction SilentlyContinue
|
| 526 |
+
|
| 527 |
+
if ($null -ne $localBinPath) {
|
| 528 |
+
Write-Host "[环境] 发现系统安装的 Aria2: $($localBinPath.Source)" -ForegroundColor Gray
|
| 529 |
+
return $localBinPath.Source
|
| 530 |
+
}
|
| 531 |
+
if (Test-Path $binPath) {
|
| 532 |
+
try {
|
| 533 |
+
& "$binPath" --version > $null
|
| 534 |
+
if ($?) {
|
| 535 |
+
Write-Host "[环境] 使用缓存的 Aria2 核心: $binPath" -ForegroundColor Gray
|
| 536 |
+
return $binPath
|
| 537 |
+
}
|
| 538 |
+
} catch { }
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
Write-Host "[环境] 正在下发 Aria2 核心组件..." -ForegroundColor Yellow
|
| 542 |
+
Invoke-WebRequest-Async -Uri $binUrl -OutFile $binPath
|
| 543 |
+
return $binPath
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
function Get-7za-Executable {
|
| 547 |
+
$binUrl = "https://modelscope.cn/models/licyks/sd-webui-all-in-one/resolve/master/7za/windows/amd64/7za.exe"
|
| 548 |
+
$binPath = Join-Path ([IO.Path]::GetTempPath()) "7za.exe"
|
| 549 |
+
$localBinPath = Get-Command 7za -ErrorAction SilentlyContinue
|
| 550 |
+
$localBinPath7z = Get-Command 7z -ErrorAction SilentlyContinue
|
| 551 |
+
|
| 552 |
+
if ($null -ne $localBinPath) {
|
| 553 |
+
Write-Host "[环境] 发现系统安装的 7za: $($localBinPath.Source)" -ForegroundColor Gray
|
| 554 |
+
return $localBinPath.Source
|
| 555 |
+
}
|
| 556 |
+
if ($null -ne $localBinPath7z) {
|
| 557 |
+
Write-Host "[环境] 发现系统安装的 7z: $($localBinPath7z.Source)" -ForegroundColor Gray
|
| 558 |
+
return $localBinPath7z.Source
|
| 559 |
+
}
|
| 560 |
+
if (Test-Path $binPath) {
|
| 561 |
+
try {
|
| 562 |
+
& "$binPath" > $null
|
| 563 |
+
if ($?) {
|
| 564 |
+
Write-Host "[环境] 使用缓存的 7-Zip 组件: $binPath" -ForegroundColor Gray
|
| 565 |
+
return $binPath
|
| 566 |
+
}
|
| 567 |
+
} catch { }
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
+
Write-Host "[环境] 正在下发 7-Zip 解压组件..." -ForegroundColor Yellow
|
| 571 |
+
Invoke-WebRequest-Async -Uri $binUrl -OutFile $binPath
|
| 572 |
+
return $binPath
|
| 573 |
+
}
|
| 574 |
+
|
| 575 |
+
function Invoke-DownloadTask {
|
| 576 |
+
param(
|
| 577 |
+
[Parameter(Mandatory)] [string]$Url,
|
| 578 |
+
[Parameter(Mandatory)] [string]$OutDir,
|
| 579 |
+
[Parameter(Mandatory)] [string]$SaveName,
|
| 580 |
+
[Parameter(Mandatory)] [Object]$State,
|
| 581 |
+
[Parameter(Mandatory)] [Object]$QueueTask
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
$bin = Get-Aria2-Executable
|
| 585 |
+
$launch_args = "-c -x 16 -s 64 -k 1M --file-allocation=none --summary-interval=0 --console-log-level=error -d `"$($OutDir.TrimEnd('\', '/'))`" `"$Url`" -o `"$SaveName`""
|
| 586 |
+
|
| 587 |
+
try {
|
| 588 |
+
Write-Host "[任务] 正在启动 Aria2 下载引擎..." -ForegroundColor Blue
|
| 589 |
+
Write-Host "[任务] 目标: $SaveName" -ForegroundColor Gray
|
| 590 |
+
|
| 591 |
+
$process = Start-Process -FilePath $bin -ArgumentList $launch_args -PassThru -NoNewWindow
|
| 592 |
+
$process.EnableRaisingEvents = $true
|
| 593 |
+
|
| 594 |
+
$taskInfo = [PSCustomObject]@{
|
| 595 |
+
Id = "Task-" + [guid]::NewGuid().Guid.Substring(0, 8)
|
| 596 |
+
Process = $process;
|
| 597 |
+
SaveName = $SaveName;
|
| 598 |
+
OutDir = $OutDir;
|
| 599 |
+
QueueTask = $QueueTask
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
$State.CurrentTask = $taskInfo
|
| 603 |
+
$State.IsCancelled = $false
|
| 604 |
+
Write-Host "[任务] 引擎启动成功 [PID: $($process.Id)]" -ForegroundColor Green
|
| 605 |
+
|
| 606 |
+
Show-Async-MsgBox -Message "任务已开始下载:`n$SaveName`n`n保存路径:`n$OutDir" -Title "下载启动"
|
| 607 |
+
return $taskInfo.Id
|
| 608 |
+
}
|
| 609 |
+
catch {
|
| 610 |
+
Write-Host "[任务] 启动下载任务失败: $($_.Exception.Message)" -ForegroundColor Red
|
| 611 |
+
$QueueTask.Status = "失败"
|
| 612 |
+
Show-Async-MsgBox -Message "无法启动下载任务:`n$($_.Exception.Message)`n`n报告问题与寻求帮助请前往: `nhttps://github.com/licyk/sd-webui-all-in-one/issues" -Title "启动失败" -Icon "Error"
|
| 613 |
+
return $null
|
| 614 |
+
}
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
function Start-ExtractionTask {
|
| 618 |
+
param([string]$FilePath, [string]$ExtractDir, [Object]$State, [Object]$QueueTask)
|
| 619 |
+
|
| 620 |
+
$bin = Get-7za-Executable
|
| 621 |
+
Write-Host "[后处理] 正在启动解压任务: $FilePath ..." -ForegroundColor Cyan
|
| 622 |
+
|
| 623 |
+
try {
|
| 624 |
+
$process = Start-Process -FilePath $bin -ArgumentList "x `"$FilePath`" -o`"$($ExtractDir.TrimEnd('\', '/'))`" -y" -PassThru -NoNewWindow
|
| 625 |
+
$process.EnableRaisingEvents = $true
|
| 626 |
+
|
| 627 |
+
$State.CurrentExtractionTask = [PSCustomObject]@{
|
| 628 |
+
Process = $process;
|
| 629 |
+
FilePath = $FilePath;
|
| 630 |
+
ExtractDir = $ExtractDir;
|
| 631 |
+
QueueTask = $QueueTask
|
| 632 |
+
}
|
| 633 |
+
Write-Host "[后处理] 解压引擎启动成功 [PID: $($process.Id)]" -ForegroundColor Green
|
| 634 |
+
return $process
|
| 635 |
+
}
|
| 636 |
+
catch {
|
| 637 |
+
Write-Host "[后处理] 启动解压失败: $($_.Exception.Message)" -ForegroundColor Red
|
| 638 |
+
$QueueTask.Status = "失败"
|
| 639 |
+
Show-Async-MsgBox -Message "无法启动解压任务:`n$($_.Exception.Message)`n`n报告问题与寻求帮助请前往: `nhttps://github.com/licyk/sd-webui-all-in-one/issues" -Title "解压失败" -Icon "Error"
|
| 640 |
+
return $null
|
| 641 |
+
}
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
function Find-Visual-Element {
|
| 645 |
+
param([System.Windows.DependencyObject]$Parent, [string]$Name)
|
| 646 |
+
if ($null -eq $Parent) { return $null }
|
| 647 |
+
for ($i = 0; $i -lt [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($Parent); $i++) {
|
| 648 |
+
$child = [System.Windows.Media.VisualTreeHelper]::GetChild($Parent, $i)
|
| 649 |
+
if ($child -is [System.Windows.FrameworkElement] -and $child.Name -eq $Name) { return $child }
|
| 650 |
+
$result = Find-Visual-Element -Parent $child -Name $Name
|
| 651 |
+
if ($null -ne $result) { return $result }
|
| 652 |
+
}
|
| 653 |
+
return $null
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
# 将同步逻辑封装为自包含的脚本块(PS5.1 兼容性最佳方案)
|
| 657 |
+
$script:SyncDataGridLogic = {
|
| 658 |
+
param($UI, $State)
|
| 659 |
+
if ($null -eq $State.Metadata) {
|
| 660 |
+
Write-Host "[数据] 无法同步:元数据为空" -ForegroundColor Red
|
| 661 |
+
return
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
$versionType = if ($UI.StableRadio.IsChecked) { "stable" } else { "nightly" }
|
| 665 |
+
$sourceName = if ($UI.HFRadio.IsChecked) { "huggingface" } else { "modelscope" }
|
| 666 |
+
|
| 667 |
+
Write-Host "[数据] 正在切换视图: $sourceName -> $versionType" -ForegroundColor Magenta
|
| 668 |
+
$gridSource = @()
|
| 669 |
+
$sourceNode = $State.Metadata."$sourceName"
|
| 670 |
+
if ($null -ne $sourceNode) {
|
| 671 |
+
$vNode = $sourceNode."$versionType"
|
| 672 |
+
if ($null -ne $vNode) {
|
| 673 |
+
foreach ($comp in $vNode.PSObject.Properties) {
|
| 674 |
+
$versions = @()
|
| 675 |
+
if ($null -ne $comp.Value -and $comp.Value -is [System.Array]) {
|
| 676 |
+
foreach ($vEntry in $comp.Value) {
|
| 677 |
+
if ($vEntry.Count -ge 2) {
|
| 678 |
+
$versions += [PSCustomObject]@{ Name = $vEntry[0]; Url = $vEntry[1] }
|
| 679 |
+
}
|
| 680 |
+
}
|
| 681 |
+
}
|
| 682 |
+
if ($versions.Count -gt 0) {
|
| 683 |
+
$gridSource += [PSCustomObject]@{
|
| 684 |
+
Type = $comp.Name
|
| 685 |
+
Versions = $versions
|
| 686 |
+
SelectedVersion = $versions[0]
|
| 687 |
+
}
|
| 688 |
+
}
|
| 689 |
+
}
|
| 690 |
+
}
|
| 691 |
+
}
|
| 692 |
+
Write-Host "[数据] 已加载 $($gridSource.Count) 个资源条目" -ForegroundColor Gray
|
| 693 |
+
$UI.MainGrid.ItemsSource = $null
|
| 694 |
+
$UI.MainGrid.ItemsSource = $gridSource
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
function Invoke-Refresh {
|
| 698 |
+
param($UI, $State)
|
| 699 |
+
|
| 700 |
+
# 显式引用脚本块,确保它在当前函数的局部作用域内,以便闭包捕获
|
| 701 |
+
$SyncLogic = $script:SyncDataGridLogic
|
| 702 |
+
|
| 703 |
+
# 显示加载状态
|
| 704 |
+
$UI.LoadingOverlay.Visibility = "Visible"
|
| 705 |
+
$UI.MainGrid.Visibility = "Collapsed"
|
| 706 |
+
$UI.RefreshBtn.IsEnabled = $false
|
| 707 |
+
$UI.UpdateTimeText.Text = "状态: 正在同步云端数据..."
|
| 708 |
+
Write-Host "[UI] 用户触发同步请求,正在发起异步任务..." -ForegroundColor Cyan
|
| 709 |
+
|
| 710 |
+
# 确保全局 RunspacePool 存在
|
| 711 |
+
if ($null -eq $Global:DownloadRunspacePool) {
|
| 712 |
+
$Global:DownloadRunspacePool = [runspacefactory]::CreateRunspacePool(1, 2)
|
| 713 |
+
$Global:DownloadRunspacePool.Open()
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
$ps = [powershell]::Create().AddScript({
|
| 717 |
+
param([string[]]$Uris)
|
| 718 |
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
| 719 |
+
$lastError = ""
|
| 720 |
+
foreach ($Uri in $Uris) {
|
| 721 |
+
try {
|
| 722 |
+
$response = Invoke-WebRequest -UseBasicParsing -Uri $Uri -TimeoutSec 15 -ErrorAction Stop
|
| 723 |
+
$json = $response.Content | ConvertFrom-Json
|
| 724 |
+
if ($null -ne $json) { return $json }
|
| 725 |
+
} catch {
|
| 726 |
+
$lastError = $_.Exception.Message
|
| 727 |
+
}
|
| 728 |
+
}
|
| 729 |
+
return "ERROR: " + $lastError
|
| 730 |
+
}).AddArgument(@(
|
| 731 |
+
"https://licyk.github.io/resources/portable_list.json",
|
| 732 |
+
"https://github.com/licyk/resources/raw/gh-pages/portable_list.json",
|
| 733 |
+
"https://gitlab.com/licyk/resources/-/raw/gh-pages/portable_list.json",
|
| 734 |
+
"https://gitee.com/licyk/resources/raw/gh-pages/portable_list.json"
|
| 735 |
+
))
|
| 736 |
+
|
| 737 |
+
$ps.RunspacePool = $Global:DownloadRunspacePool
|
| 738 |
+
$asyncResult = $ps.BeginInvoke()
|
| 739 |
+
|
| 740 |
+
$monitorTimer = New-Object System.Windows.Threading.DispatcherTimer
|
| 741 |
+
$monitorTimer.Interval = [TimeSpan]::FromMilliseconds(200)
|
| 742 |
+
|
| 743 |
+
$tickAction = {
|
| 744 |
+
if ($asyncResult.IsCompleted) {
|
| 745 |
+
$monitorTimer.Stop()
|
| 746 |
+
Write-Host "[UI] 云端数据已到达 [OK]" -ForegroundColor Green
|
| 747 |
+
try {
|
| 748 |
+
$result = $ps.EndInvoke($asyncResult)
|
| 749 |
+
|
| 750 |
+
if ($null -ne $result -and $null -ne $result.update_time) {
|
| 751 |
+
Write-Host "[数据] 解析成功,更新时间: $($result.update_time)" -ForegroundColor Gray
|
| 752 |
+
$State.Metadata = $result
|
| 753 |
+
$UI.UpdateTimeText.Text = "更新时间: $($result.update_time)"
|
| 754 |
+
# 调用捕获到的脚本块
|
| 755 |
+
& $SyncLogic -UI $UI -State $State
|
| 756 |
+
$UI.UpdateTimeText.Foreground = [System.Windows.Media.Brushes]::Gray
|
| 757 |
+
} elseif ($result -is [string] -and $result.StartsWith("ERROR:")) {
|
| 758 |
+
Write-Host "[数据] 同步异常: $result" -ForegroundColor Red
|
| 759 |
+
$UI.UpdateTimeText.Text = "同步失败: " + $result.Substring(6)
|
| 760 |
+
$UI.UpdateTimeText.Foreground = [System.Windows.Media.Brushes]::Red
|
| 761 |
+
} else {
|
| 762 |
+
Write-Host "[数据] 获取到的数据格式不匹配" -ForegroundColor Red
|
| 763 |
+
$UI.UpdateTimeText.Text = "同步失败: 获取到的数据格式不正确"
|
| 764 |
+
$UI.UpdateTimeText.Foreground = [System.Windows.Media.Brushes]::Red
|
| 765 |
+
}
|
| 766 |
+
} catch {
|
| 767 |
+
Write-Host "[数据] 内部解析错误: $($_.Exception.Message)" -ForegroundColor Red
|
| 768 |
+
$UI.UpdateTimeText.Text = "内部错误: $($_.Exception.Message)"
|
| 769 |
+
$UI.UpdateTimeText.Foreground = [System.Windows.Media.Brushes]::Red
|
| 770 |
+
} finally {
|
| 771 |
+
$ps.Dispose()
|
| 772 |
+
$UI.LoadingOverlay.Visibility = "Collapsed"
|
| 773 |
+
$UI.MainGrid.Visibility = "Visible"
|
| 774 |
+
$UI.RefreshBtn.IsEnabled = $true
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
}.GetNewClosure()
|
| 778 |
+
|
| 779 |
+
$monitorTimer.Add_Tick($tickAction)
|
| 780 |
+
$monitorTimer.Start()
|
| 781 |
+
}
|
| 782 |
+
|
| 783 |
+
function Invoke-DownloadAction {
|
| 784 |
+
param($UI, $State, $Button)
|
| 785 |
+
$savePath = $UI.PathInput.Text
|
| 786 |
+
if (-not (Test-Path $savePath -PathType Container)) {
|
| 787 |
+
Write-Host "[UI] 拦截下载请求:保存路径无效 [$savePath]" -ForegroundColor Red
|
| 788 |
+
Show-Async-MsgBox -Message "下载路径无效。" -Title "错误" -Icon "Error"
|
| 789 |
+
return
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
$rowData = $Button.DataContext
|
| 793 |
+
$selected = $rowData.SelectedVersion
|
| 794 |
+
Write-Host "[UI] 用户点击添加任务: $($selected.Name)" -ForegroundColor Cyan
|
| 795 |
+
|
| 796 |
+
$newTask = [PSCustomObject]@{
|
| 797 |
+
Name = $selected.Name
|
| 798 |
+
Url = $selected.Url
|
| 799 |
+
OutDir = $savePath
|
| 800 |
+
AutoExtract = $UI.AutoExtract.IsChecked
|
| 801 |
+
AutoDelete = $UI.AutoDelete.IsChecked
|
| 802 |
+
Status = "等待中"
|
| 803 |
+
Progress = "0%"
|
| 804 |
+
}
|
| 805 |
+
|
| 806 |
+
$State.TaskQueue += $newTask
|
| 807 |
+
& $script:UpdateQueueUI -UI $UI -State $State
|
| 808 |
+
Show-Async-MsgBox -Message "任务已加入队列:`n$($selected.Name)`n`n队列任务将按顺序自动执行。" -Title "任务已添加"
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
+
function Set-Proxy {
|
| 812 |
+
$Env:NO_PROXY = "localhost,127.0.0.1,::1"
|
| 813 |
+
|
| 814 |
+
$internet_setting = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
|
| 815 |
+
if ($internet_setting.ProxyEnable -ne 1) {
|
| 816 |
+
Write-Host "[环境] 未从系统中检测到代理,跳过设置代理" -ForegroundColor Blue
|
| 817 |
+
return
|
| 818 |
+
}
|
| 819 |
+
$proxy_addr = $($internet_setting.ProxyServer)
|
| 820 |
+
# 提取代理地址
|
| 821 |
+
if (($proxy_addr -match "http=(.*?);") -or ($proxy_addr -match "https=(.*?);")) {
|
| 822 |
+
$proxy_value = $matches[1]
|
| 823 |
+
# 去除 http / https 前缀
|
| 824 |
+
$proxy_value = $proxy_value.ToString().Replace("http://", "").Replace("https://", "")
|
| 825 |
+
$proxy_value = "http://${proxy_value}"
|
| 826 |
+
} elseif ($proxy_addr -match "socks=(.*)") {
|
| 827 |
+
$proxy_value = $matches[1]
|
| 828 |
+
# 去除 socks 前缀
|
| 829 |
+
$proxy_value = $proxy_value.ToString().Replace("http://", "").Replace("https://", "")
|
| 830 |
+
$proxy_value = "socks://${proxy_value}"
|
| 831 |
+
} else {
|
| 832 |
+
$proxy_value = "http://${proxy_addr}"
|
| 833 |
+
}
|
| 834 |
+
$Env:HTTP_PROXY = $proxy_value
|
| 835 |
+
$Env:HTTPS_PROXY = $proxy_value
|
| 836 |
+
Write-Host "[环境] 检测到系统设置了代理,已读取系统中的代理配置并设置代理,代理地址: $proxy_value" -ForegroundColor Green
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
function Start-App {
|
| 840 |
+
Write-Host "[APP] 初始化中..." -ForegroundColor Yellow
|
| 841 |
+
Set-Proxy
|
| 842 |
+
# 实时检测 Windows 深色模式
|
| 843 |
+
$isDarkMode = $false
|
| 844 |
+
try {
|
| 845 |
+
$reg = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" -ErrorAction SilentlyContinue
|
| 846 |
+
if ($null -ne $reg -and $reg.AppsUseLightTheme -eq 0) { $isDarkMode = $true }
|
| 847 |
+
} catch {}
|
| 848 |
+
|
| 849 |
+
# 主题颜色定义
|
| 850 |
+
$colors = if ($isDarkMode) {
|
| 851 |
+
Write-Host "[环境] 应用深色主题 [Dark Mode]" -ForegroundColor Gray
|
| 852 |
+
@{
|
| 853 |
+
WinBG1="#CC1E1E1E"; WinBG2="#CC121212"; PanelBG="#44000000"; TextMain="#FFFFFF"; TextSec="#AAAAAA"; Border="#44FFFFFF"; InputBG="#333333"; BtnNormal="#4A4A4A"; BtnHover="#5A5A5A"; ItemHover="#33FFFFFF"; HeaderBG="#11FFFFFF";
|
| 854 |
+
ScrollBG="#11FFFFFF"; ScrollThumb="#66FFFFFF"
|
| 855 |
+
}
|
| 856 |
+
} else {
|
| 857 |
+
Write-Host "[环境] ���用浅色主题 [Light Mode]" -ForegroundColor Gray
|
| 858 |
+
@{
|
| 859 |
+
WinBG1="#CCF9FAFB"; WinBG2="#CCF3F4F6"; PanelBG="#44FFFFFF"; TextMain="#323130"; TextSec="#666666"; Border="#88C1C1C1"; InputBG="#FFFFFF"; BtnNormal="#FFFFFF"; BtnHover="#F9F9F9"; ItemHover="#F2F7FF"; HeaderBG="#F9FAFB";
|
| 860 |
+
ScrollBG="#05000000"; ScrollThumb="#AAAAAA"
|
| 861 |
+
}
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
[xml]$xaml = @"
|
| 865 |
+
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
| 866 |
+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
| 867 |
+
Title="AI 整合包下载器" Height="700" Width="900"
|
| 868 |
+
MinHeight="500" MinWidth="700"
|
| 869 |
+
WindowStartupLocation="CenterScreen" WindowStyle="None" AllowsTransparency="True"
|
| 870 |
+
Background="Transparent" ResizeMode="CanResizeWithGrip">
|
| 871 |
+
<Window.Resources>
|
| 872 |
+
<!-- 基础色彩注入 -->
|
| 873 |
+
<SolidColorBrush x:Key="PrimaryBrush" Color="#0078D4"/>
|
| 874 |
+
<SolidColorBrush x:Key="TextMainBrush" Color="$($colors.TextMain)"/>
|
| 875 |
+
<SolidColorBrush x:Key="TextSecBrush" Color="$($colors.TextSec)"/>
|
| 876 |
+
<SolidColorBrush x:Key="BorderBrush" Color="$($colors.Border)"/>
|
| 877 |
+
<SolidColorBrush x:Key="InputBGBrush" Color="$($colors.InputBG)"/>
|
| 878 |
+
<SolidColorBrush x:Key="BtnNormalBrush" Color="$($colors.BtnNormal)"/>
|
| 879 |
+
<SolidColorBrush x:Key="PanelBGBrush" Color="$($colors.PanelBG)"/>
|
| 880 |
+
|
| 881 |
+
<!-- 现代滚动条样式 -->
|
| 882 |
+
<Style TargetType="ScrollBar">
|
| 883 |
+
<Setter Property="Background" Value="$($colors.ScrollBG)"/>
|
| 884 |
+
<Setter Property="Width" Value="8"/>
|
| 885 |
+
<Setter Property="Template">
|
| 886 |
+
<Setter.Value>
|
| 887 |
+
<ControlTemplate TargetType="ScrollBar">
|
| 888 |
+
<Grid Name="Bg" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
|
| 889 |
+
<Track Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsEnabled}">
|
| 890 |
+
<Track.Thumb>
|
| 891 |
+
<Thumb>
|
| 892 |
+
<Thumb.Template>
|
| 893 |
+
<ControlTemplate TargetType="Thumb">
|
| 894 |
+
<Border Background="$($colors.ScrollThumb)" CornerRadius="4" Margin="1,0"/>
|
| 895 |
+
</ControlTemplate>
|
| 896 |
+
</Thumb.Template>
|
| 897 |
+
</Thumb>
|
| 898 |
+
</Track.Thumb>
|
| 899 |
+
</Track>
|
| 900 |
+
</Grid>
|
| 901 |
+
</ControlTemplate>
|
| 902 |
+
</Setter.Value>
|
| 903 |
+
</Setter>
|
| 904 |
+
</Style>
|
| 905 |
+
|
| 906 |
+
<!-- 按钮样式 -->
|
| 907 |
+
<Style TargetType="Button">
|
| 908 |
+
<Setter Property="Background" Value="{DynamicResource BtnNormalBrush}"/>
|
| 909 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 910 |
+
<Setter Property="BorderThickness" Value="1"/>
|
| 911 |
+
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
|
| 912 |
+
<Setter Property="Padding" Value="12,6"/>
|
| 913 |
+
<Setter Property="FontSize" Value="13"/>
|
| 914 |
+
<Setter Property="Template">
|
| 915 |
+
<Setter.Value>
|
| 916 |
+
<ControlTemplate TargetType="Button">
|
| 917 |
+
<Border Name="border" Background="{TemplateBinding Background}"
|
| 918 |
+
BorderBrush="{TemplateBinding BorderBrush}"
|
| 919 |
+
BorderThickness="{TemplateBinding BorderThickness}"
|
| 920 |
+
CornerRadius="6" SnapsToDevicePixels="True">
|
| 921 |
+
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}">
|
| 922 |
+
<ContentPresenter.Resources>
|
| 923 |
+
<Style TargetType="TextBlock">
|
| 924 |
+
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
|
| 925 |
+
</Style>
|
| 926 |
+
</ContentPresenter.Resources>
|
| 927 |
+
</ContentPresenter>
|
| 928 |
+
</Border>
|
| 929 |
+
<ControlTemplate.Triggers>
|
| 930 |
+
<Trigger Property="IsMouseOver" Value="True">
|
| 931 |
+
<Setter TargetName="border" Property="Background" Value="$($colors.BtnHover)"/>
|
| 932 |
+
</Trigger>
|
| 933 |
+
<Trigger Property="IsPressed" Value="True">
|
| 934 |
+
<Setter TargetName="border" Property="RenderTransform">
|
| 935 |
+
<Setter.Value><ScaleTransform ScaleX="0.98" ScaleY="0.98"/></Setter.Value>
|
| 936 |
+
</Setter>
|
| 937 |
+
</Trigger>
|
| 938 |
+
<Trigger Property="IsEnabled" Value="False">
|
| 939 |
+
<Setter Property="Opacity" Value="0.5"/>
|
| 940 |
+
</Trigger>
|
| 941 |
+
</ControlTemplate.Triggers>
|
| 942 |
+
</ControlTemplate>
|
| 943 |
+
</Setter.Value>
|
| 944 |
+
</Setter>
|
| 945 |
+
</Style>
|
| 946 |
+
|
| 947 |
+
<!-- 主按钮样式 (蓝色) -->
|
| 948 |
+
<Style x:Key="PrimaryButton" TargetType="Button">
|
| 949 |
+
<Setter Property="Background" Value="#0078D4"/>
|
| 950 |
+
<Setter Property="Foreground" Value="White"/>
|
| 951 |
+
<Setter Property="BorderThickness" Value="0"/>
|
| 952 |
+
<Setter Property="Padding" Value="12,6"/>
|
| 953 |
+
<Setter Property="FontSize" Value="13"/>
|
| 954 |
+
<Setter Property="Template">
|
| 955 |
+
<Setter.Value>
|
| 956 |
+
<ControlTemplate TargetType="Button">
|
| 957 |
+
<Border Name="border" Background="{TemplateBinding Background}" CornerRadius="6" SnapsToDevicePixels="True">
|
| 958 |
+
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
|
| 959 |
+
</Border>
|
| 960 |
+
<ControlTemplate.Triggers>
|
| 961 |
+
<Trigger Property="IsMouseOver" Value="True">
|
| 962 |
+
<Setter TargetName="border" Property="Background" Value="#2B88D8"/>
|
| 963 |
+
</Trigger>
|
| 964 |
+
<Trigger Property="IsPressed" Value="True">
|
| 965 |
+
<Setter TargetName="border" Property="Background" Value="#005A9E"/>
|
| 966 |
+
<Setter TargetName="border" Property="RenderTransform">
|
| 967 |
+
<Setter.Value><ScaleTransform ScaleX="0.97" ScaleY="0.97"/></Setter.Value>
|
| 968 |
+
</Setter>
|
| 969 |
+
</Trigger>
|
| 970 |
+
<Trigger Property="IsEnabled" Value="False">
|
| 971 |
+
<Setter Property="Opacity" Value="0.5"/>
|
| 972 |
+
</Trigger>
|
| 973 |
+
</ControlTemplate.Triggers>
|
| 974 |
+
</ControlTemplate>
|
| 975 |
+
</Setter.Value>
|
| 976 |
+
</Setter>
|
| 977 |
+
</Style>
|
| 978 |
+
|
| 979 |
+
<!-- 输入框样式 -->
|
| 980 |
+
<Style TargetType="TextBox">
|
| 981 |
+
<Setter Property="Padding" Value="8,5"/>
|
| 982 |
+
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
| 983 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 984 |
+
<Setter Property="Template">
|
| 985 |
+
<Setter.Value>
|
| 986 |
+
<ControlTemplate TargetType="TextBox">
|
| 987 |
+
<Border Name="border" Background="{DynamicResource InputBGBrush}" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="4">
|
| 988 |
+
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
|
| 989 |
+
</Border>
|
| 990 |
+
<ControlTemplate.Triggers>
|
| 991 |
+
<Trigger Property="IsFocused" Value="True">
|
| 992 |
+
<Setter TargetName="border" Property="BorderBrush" Value="#0078D4"/>
|
| 993 |
+
<Setter TargetName="border" Property="BorderThickness" Value="2"/>
|
| 994 |
+
</Trigger>
|
| 995 |
+
</ControlTemplate.Triggers>
|
| 996 |
+
</ControlTemplate>
|
| 997 |
+
</Setter.Value>
|
| 998 |
+
</Setter>
|
| 999 |
+
</Style>
|
| 1000 |
+
|
| 1001 |
+
<!-- 下拉框样式 -->
|
| 1002 |
+
<Style TargetType="ComboBox">
|
| 1003 |
+
<Setter Property="Padding" Value="8,4"/>
|
| 1004 |
+
<Setter Property="MinHeight" Value="28"/>
|
| 1005 |
+
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
| 1006 |
+
<Setter Property="Background" Value="{DynamicResource InputBGBrush}"/>
|
| 1007 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 1008 |
+
<Setter Property="Template">
|
| 1009 |
+
<Setter.Value>
|
| 1010 |
+
<ControlTemplate TargetType="ComboBox">
|
| 1011 |
+
<Grid>
|
| 1012 |
+
<ToggleButton Name="ToggleButton" Background="{TemplateBinding Background}"
|
| 1013 |
+
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
|
| 1014 |
+
IsChecked="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
|
| 1015 |
+
Focusable="False" ClickMode="Press">
|
| 1016 |
+
<ToggleButton.Style>
|
| 1017 |
+
<Style TargetType="ToggleButton">
|
| 1018 |
+
<Setter Property="Template">
|
| 1019 |
+
<Setter.Value>
|
| 1020 |
+
<ControlTemplate TargetType="ToggleButton">
|
| 1021 |
+
<Border Name="border" Background="{TemplateBinding Background}"
|
| 1022 |
+
BorderBrush="{TemplateBinding BorderBrush}"
|
| 1023 |
+
BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
|
| 1024 |
+
<Path Name="Arrow" Fill="{DynamicResource TextSecBrush}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
| 1025 |
+
Margin="0,0,8,0" Data="M 0 0 L 4 4 L 8 0 Z"/>
|
| 1026 |
+
</Border>
|
| 1027 |
+
<ControlTemplate.Triggers>
|
| 1028 |
+
<Trigger Property="IsMouseOver" Value="True">
|
| 1029 |
+
<Setter TargetName="border" Property="Background" Value="$($colors.BtnHover)"/>
|
| 1030 |
+
</Trigger>
|
| 1031 |
+
<Trigger Property="IsChecked" Value="True">
|
| 1032 |
+
<Setter TargetName="Arrow" Property="RenderTransform">
|
| 1033 |
+
<Setter.Value><RotateTransform Angle="180" CenterX="4" CenterY="2"/></Setter.Value>
|
| 1034 |
+
</Setter>
|
| 1035 |
+
</Trigger>
|
| 1036 |
+
</ControlTemplate.Triggers>
|
| 1037 |
+
</ControlTemplate>
|
| 1038 |
+
</Setter.Value>
|
| 1039 |
+
</Setter>
|
| 1040 |
+
</Style>
|
| 1041 |
+
</ToggleButton.Style>
|
| 1042 |
+
</ToggleButton>
|
| 1043 |
+
<ContentPresenter Name="ContentSite" IsHitTestVisible="False"
|
| 1044 |
+
Content="{TemplateBinding SelectionBoxItem}"
|
| 1045 |
+
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
|
| 1046 |
+
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
|
| 1047 |
+
Margin="{TemplateBinding Padding}" VerticalAlignment="Center"/>
|
| 1048 |
+
<Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
|
| 1049 |
+
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
|
| 1050 |
+
<Border Name="DropDownBorder" Background="{DynamicResource InputBGBrush}" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" CornerRadius="6" Margin="0,2,0,5">
|
| 1051 |
+
<Border.Effect><DropShadowEffect BlurRadius="5" ShadowDepth="2" Opacity="0.15"/></Border.Effect>
|
| 1052 |
+
<ScrollViewer Margin="2" SnapsToDevicePixels="True">
|
| 1053 |
+
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
|
| 1054 |
+
</ScrollViewer>
|
| 1055 |
+
</Border>
|
| 1056 |
+
</Grid>
|
| 1057 |
+
</Popup>
|
| 1058 |
+
</Grid>
|
| 1059 |
+
<ControlTemplate.Triggers>
|
| 1060 |
+
<Trigger Property="IsMouseOver" Value="True">
|
| 1061 |
+
<Setter Property="BorderBrush" Value="#A1A1A1"/>
|
| 1062 |
+
</Trigger>
|
| 1063 |
+
<Trigger Property="IsFocused" Value="True">
|
| 1064 |
+
<Setter Property="BorderBrush" Value="#0078D4"/>
|
| 1065 |
+
</Trigger>
|
| 1066 |
+
</ControlTemplate.Triggers>
|
| 1067 |
+
</ControlTemplate>
|
| 1068 |
+
</Setter.Value>
|
| 1069 |
+
</Setter>
|
| 1070 |
+
</Style>
|
| 1071 |
+
|
| 1072 |
+
<!-- 下拉列表项样式 -->
|
| 1073 |
+
<Style TargetType="ComboBoxItem">
|
| 1074 |
+
<Setter Property="Padding" Value="10,12"/>
|
| 1075 |
+
<Setter Property="MinHeight" Value="34"/>
|
| 1076 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 1077 |
+
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
| 1078 |
+
<Setter Property="Template">
|
| 1079 |
+
<Setter.Value>
|
| 1080 |
+
<ControlTemplate TargetType="ComboBoxItem">
|
| 1081 |
+
<Border Name="Border" Background="Transparent" CornerRadius="4" Margin="2,1">
|
| 1082 |
+
<ContentPresenter VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
|
| 1083 |
+
</Border>
|
| 1084 |
+
<ControlTemplate.Triggers>
|
| 1085 |
+
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="Border" Property="Background" Value="$($colors.ItemHover)"/></Trigger>
|
| 1086 |
+
<Trigger Property="IsSelected" Value="True"><Setter TargetName="Border" Property="Background" Value="#0078D4"/><Setter Property="TextElement.Foreground" Value="White"/></Trigger>
|
| 1087 |
+
</ControlTemplate.Triggers>
|
| 1088 |
+
</ControlTemplate>
|
| 1089 |
+
</Setter.Value>
|
| 1090 |
+
</Setter>
|
| 1091 |
+
</Style>
|
| 1092 |
+
|
| 1093 |
+
<!-- 单选按钮药丸样式 -->
|
| 1094 |
+
<Style TargetType="RadioButton">
|
| 1095 |
+
<Setter Property="FontSize" Value="12"/>
|
| 1096 |
+
<Setter Property="Height" Value="26"/>
|
| 1097 |
+
<Setter Property="Cursor" Value="Hand"/>
|
| 1098 |
+
<Setter Property="Foreground" Value="{DynamicResource TextSecBrush}"/>
|
| 1099 |
+
<Setter Property="Template">
|
| 1100 |
+
<Setter.Value>
|
| 1101 |
+
<ControlTemplate TargetType="RadioButton">
|
| 1102 |
+
<Border Name="border" Background="{DynamicResource BtnNormalBrush}" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="13" Padding="12,2" Margin="0,0,5,0">
|
| 1103 |
+
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
| 1104 |
+
</Border>
|
| 1105 |
+
<ControlTemplate.Triggers>
|
| 1106 |
+
<Trigger Property="IsChecked" Value="True">
|
| 1107 |
+
<Setter TargetName="border" Property="Background" Value="#0078D4"/>
|
| 1108 |
+
<Setter TargetName="border" Property="BorderBrush" Value="#0078D4"/>
|
| 1109 |
+
<Setter Property="Foreground" Value="White"/>
|
| 1110 |
+
<Setter Property="FontWeight" Value="SemiBold"/>
|
| 1111 |
+
</Trigger>
|
| 1112 |
+
<MultiTrigger>
|
| 1113 |
+
<MultiTrigger.Conditions>
|
| 1114 |
+
<Condition Property="IsMouseOver" Value="True"/>
|
| 1115 |
+
<Condition Property="IsChecked" Value="False"/>
|
| 1116 |
+
</MultiTrigger.Conditions>
|
| 1117 |
+
<Setter TargetName="border" Property="Background" Value="$($colors.BtnHover)"/>
|
| 1118 |
+
</MultiTrigger>
|
| 1119 |
+
</ControlTemplate.Triggers>
|
| 1120 |
+
</ControlTemplate>
|
| 1121 |
+
</Setter.Value>
|
| 1122 |
+
</Setter>
|
| 1123 |
+
</Style>
|
| 1124 |
+
|
| 1125 |
+
<!-- 开关样式 (之前定义的) -->
|
| 1126 |
+
<Style x:Key="ToggleSwitchStyle" TargetType="CheckBox">
|
| 1127 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 1128 |
+
<Setter Property="Template">
|
| 1129 |
+
<Setter.Value>
|
| 1130 |
+
<ControlTemplate TargetType="CheckBox">
|
| 1131 |
+
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
| 1132 |
+
<Grid Name="SwitchRoot" Width="42" Height="22" Cursor="Hand">
|
| 1133 |
+
<Border Name="Track" Background="{DynamicResource BorderBrush}" CornerRadius="11" BorderThickness="0"/>
|
| 1134 |
+
<Border Name="Thumb" Background="White" Width="18" Height="18" CornerRadius="9" HorizontalAlignment="Left" Margin="2,0,0,0">
|
| 1135 |
+
<Border.Effect><DropShadowEffect ShadowDepth="1" BlurRadius="3" Opacity="0.2"/></Border.Effect>
|
| 1136 |
+
</Border>
|
| 1137 |
+
</Grid>
|
| 1138 |
+
<ContentPresenter Name="Content" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
| 1139 |
+
</StackPanel>
|
| 1140 |
+
<ControlTemplate.Triggers>
|
| 1141 |
+
<Trigger Property="IsChecked" Value="True">
|
| 1142 |
+
<Trigger.EnterActions><BeginStoryboard><Storyboard>
|
| 1143 |
+
<ColorAnimation Storyboard.TargetName="Track" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="#0078D4" Duration="0:0:0.2"/>
|
| 1144 |
+
<ThicknessAnimation Storyboard.TargetName="Thumb" Storyboard.TargetProperty="Margin" To="22,0,0,0" Duration="0:0:0.2"/>
|
| 1145 |
+
</Storyboard></BeginStoryboard></Trigger.EnterActions>
|
| 1146 |
+
<Trigger.ExitActions><BeginStoryboard><Storyboard>
|
| 1147 |
+
<ColorAnimation Storyboard.TargetName="Track" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="$($colors.Border)" Duration="0:0:0.2"/>
|
| 1148 |
+
<ThicknessAnimation Storyboard.TargetName="Thumb" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.2"/>
|
| 1149 |
+
</Storyboard></BeginStoryboard></Trigger.ExitActions>
|
| 1150 |
+
</Trigger>
|
| 1151 |
+
</ControlTemplate.Triggers>
|
| 1152 |
+
</ControlTemplate>
|
| 1153 |
+
</Setter.Value>
|
| 1154 |
+
</Setter>
|
| 1155 |
+
</Style>
|
| 1156 |
+
</Window.Resources>
|
| 1157 |
+
|
| 1158 |
+
<!-- 主容器 -->
|
| 1159 |
+
<Border x:Name="MainBorder" CornerRadius="12" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" ClipToBounds="True">
|
| 1160 |
+
<Border.Background>
|
| 1161 |
+
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
|
| 1162 |
+
<GradientStop Color="$($colors.WinBG1)" Offset="0.0"/>
|
| 1163 |
+
<GradientStop Color="$($colors.WinBG2)" Offset="1.0"/>
|
| 1164 |
+
</LinearGradientBrush>
|
| 1165 |
+
</Border.Background>
|
| 1166 |
+
|
| 1167 |
+
<Grid>
|
| 1168 |
+
<Grid.RowDefinitions><RowDefinition Height="32"/><RowDefinition Height="*"/></Grid.RowDefinitions>
|
| 1169 |
+
<Grid Name="TitleBar" Grid.Row="0" Background="Transparent">
|
| 1170 |
+
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="12,0,0,0">
|
| 1171 |
+
<!-- 现代化的下载图标 (圆形+箭头) -->
|
| 1172 |
+
<Path Data="M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M12,17L7,12H10V8H14V12H17L12,17Z"
|
| 1173 |
+
Fill="{DynamicResource PrimaryBrush}" Width="18" Height="18" Stretch="Uniform" Margin="0,0,8,0"/>
|
| 1174 |
+
<TextBlock Text="AI 整合包下载器" FontSize="12" Foreground="{DynamicResource TextSecBrush}" IsHitTestVisible="False"/>
|
| 1175 |
+
</StackPanel>
|
| 1176 |
+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
| 1177 |
+
<!-- 最小化 -->
|
| 1178 |
+
<Button Name="MinBtn" Content="—" Width="45" Height="32" Background="Transparent" BorderThickness="0" Foreground="{DynamicResource TextSecBrush}">
|
| 1179 |
+
<Button.Style><Style TargetType="Button"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border Name="b" Background="{TemplateBinding Background}"><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="Background" Value="$($colors.BtnHover)"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></Button.Style>
|
| 1180 |
+
</Button>
|
| 1181 |
+
<!-- 最大化/还原 -->
|
| 1182 |
+
<Button Name="MaxBtn" Content="⬜" Width="45" Height="32" Background="Transparent" BorderThickness="0" Foreground="{DynamicResource TextSecBrush}">
|
| 1183 |
+
<Button.Style><Style TargetType="Button"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border Name="b" Background="{TemplateBinding Background}"><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="Background" Value="$($colors.BtnHover)"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></Button.Style>
|
| 1184 |
+
</Button>
|
| 1185 |
+
<!-- 关闭 -->
|
| 1186 |
+
<Button Name="CloseBtn" Content="✕" Width="45" Height="32" Background="Transparent" BorderThickness="0" Foreground="{DynamicResource TextSecBrush}">
|
| 1187 |
+
<Button.Style><Style TargetType="Button"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border Name="b" Background="{TemplateBinding Background}"><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="Background" Value="#E81123"/><Setter Property="Foreground" Value="White"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></Button.Style>
|
| 1188 |
+
</Button>
|
| 1189 |
+
</StackPanel>
|
| 1190 |
+
</Grid>
|
| 1191 |
+
|
| 1192 |
+
<Grid Grid.Row="1" Margin="20,5,20,20">
|
| 1193 |
+
<Grid.RowDefinitions>
|
| 1194 |
+
<RowDefinition Height="Auto"/>
|
| 1195 |
+
<RowDefinition Height="*"/>
|
| 1196 |
+
<RowDefinition Height="Auto"/>
|
| 1197 |
+
</Grid.RowDefinitions>
|
| 1198 |
+
|
| 1199 |
+
<!-- 顶部信息 -->
|
| 1200 |
+
<Grid Grid.Row="0" Margin="0,0,0,15">
|
| 1201 |
+
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
|
| 1202 |
+
<StackPanel Grid.Column="0">
|
| 1203 |
+
<TextBlock Name="UpdateTime" Text="状态: 正在初始化..." FontSize="11" Foreground="{DynamicResource TextSecBrush}"/>
|
| 1204 |
+
<StackPanel Orientation="Horizontal" Margin="0,12">
|
| 1205 |
+
<TextBlock Text="版本:" VerticalAlignment="Center" Foreground="{DynamicResource TextMainBrush}" Margin="0,0,10,0"/>
|
| 1206 |
+
<RadioButton Name="Stable" Content="Stable" GroupName="V" VerticalAlignment="Center"/>
|
| 1207 |
+
<RadioButton Name="Nightly" Content="Nightly" IsChecked="True" GroupName="V" Margin="12,0" VerticalAlignment="Center"/>
|
| 1208 |
+
<Border Width="1" Height="14" Background="{DynamicResource BorderBrush}" Margin="10,0,20,0"/>
|
| 1209 |
+
<TextBlock Text="下载源:" VerticalAlignment="Center" Foreground="{DynamicResource TextMainBrush}" Margin="0,0,10,0"/>
|
| 1210 |
+
<RadioButton Name="HF" Content="HuggingFace" GroupName="S" VerticalAlignment="Center"/>
|
| 1211 |
+
<RadioButton Name="MS" Content="ModelScope" IsChecked="True" GroupName="S" Margin="12,0" VerticalAlignment="Center"/>
|
| 1212 |
+
</StackPanel>
|
| 1213 |
+
</StackPanel>
|
| 1214 |
+
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top">
|
| 1215 |
+
<Button Name="ProjBtn" Content="项目主页" Margin="0,0,8,0" Padding="10,4" Background="{DynamicResource BtnNormalBrush}"/>
|
| 1216 |
+
<Button Name="DocBtn" Content="使用说明" Margin="0,0,8,0" Padding="10,4" Background="{DynamicResource BtnNormalBrush}"/>
|
| 1217 |
+
<Button Name="LauncherBtn" Content="启动器下载" Margin="0,0,8,0" Padding="10,4" Background="{DynamicResource BtnNormalBrush}"/>
|
| 1218 |
+
<Button Name="IssueBtn" Content="问题反馈" Padding="10,4" Background="{DynamicResource BtnNormalBrush}"/>
|
| 1219 |
+
</StackPanel>
|
| 1220 |
+
</Grid>
|
| 1221 |
+
|
| 1222 |
+
<!-- 数据与队列容器 -->
|
| 1223 |
+
<Grid Name="DataQueueGrid" Grid.Row="1">
|
| 1224 |
+
<Grid.RowDefinitions>
|
| 1225 |
+
<RowDefinition Height="*"/>
|
| 1226 |
+
<RowDefinition Height="Auto"/>
|
| 1227 |
+
<RowDefinition Height="Auto"/>
|
| 1228 |
+
</Grid.RowDefinitions>
|
| 1229 |
+
|
| 1230 |
+
<!-- 数据表格 -->
|
| 1231 |
+
<Border Grid.Row="0" Background="{DynamicResource PanelBGBrush}" CornerRadius="8" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}">
|
| 1232 |
+
<Grid>
|
| 1233 |
+
<DataGrid Name="MainGrid" AutoGenerateColumns="False" IsReadOnly="True"
|
| 1234 |
+
HeadersVisibility="Column" GridLinesVisibility="None" Background="Transparent"
|
| 1235 |
+
BorderThickness="0" RowHeight="42" Visibility="Collapsed"
|
| 1236 |
+
SelectionMode="Single" SelectionUnit="FullRow">
|
| 1237 |
+
<DataGrid.Resources>
|
| 1238 |
+
<Style TargetType="DataGridCell">
|
| 1239 |
+
<Setter Property="Background" Value="Transparent"/>
|
| 1240 |
+
<Setter Property="BorderThickness" Value="0"/>
|
| 1241 |
+
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
| 1242 |
+
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
| 1243 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 1244 |
+
<Setter Property="Template">
|
| 1245 |
+
<Setter.Value>
|
| 1246 |
+
<ControlTemplate TargetType="DataGridCell">
|
| 1247 |
+
<Border Background="{TemplateBinding Background}" BorderThickness="0" SnapsToDevicePixels="True">
|
| 1248 |
+
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0"/>
|
| 1249 |
+
</Border>
|
| 1250 |
+
</ControlTemplate>
|
| 1251 |
+
</Setter.Value>
|
| 1252 |
+
</Setter>
|
| 1253 |
+
<Style.Triggers>
|
| 1254 |
+
<Trigger Property="IsSelected" Value="True"><Setter Property="Background" Value="Transparent"/><Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/></Trigger>
|
| 1255 |
+
</Style.Triggers>
|
| 1256 |
+
</Style>
|
| 1257 |
+
<Style TargetType="DataGridColumnHeader">
|
| 1258 |
+
<Setter Property="Background" Value="{DynamicResource PanelBGBrush}"/>
|
| 1259 |
+
<Setter Property="Foreground" Value="{DynamicResource TextSecBrush}"/>
|
| 1260 |
+
<Setter Property="Padding" Value="10,8"/>
|
| 1261 |
+
<Setter Property="FontSize" Value="12"/>
|
| 1262 |
+
<Setter Property="FontWeight" Value="SemiBold"/>
|
| 1263 |
+
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
| 1264 |
+
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
|
| 1265 |
+
</Style>
|
| 1266 |
+
<Style TargetType="DataGridRow">
|
| 1267 |
+
<Setter Property="Background" Value="Transparent"/>
|
| 1268 |
+
<Setter Property="Focusable" Value="False"/>
|
| 1269 |
+
<Style.Triggers>
|
| 1270 |
+
<Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="$($colors.ItemHover)"/></Trigger>
|
| 1271 |
+
<Trigger Property="IsSelected" Value="True"><Setter Property="Background" Value="Transparent"/></Trigger>
|
| 1272 |
+
</Style.Triggers>
|
| 1273 |
+
</Style>
|
| 1274 |
+
</DataGrid.Resources>
|
| 1275 |
+
<DataGrid.Columns>
|
| 1276 |
+
<DataGridTextColumn Header="资源类型" Binding="{Binding Type}" Width="250"/>
|
| 1277 |
+
<DataGridTemplateColumn Header="版本选择" Width="*">
|
| 1278 |
+
<DataGridTemplateColumn.CellTemplate>
|
| 1279 |
+
<DataTemplate>
|
| 1280 |
+
<ComboBox Name="VersionSelector" ItemsSource="{Binding Versions}"
|
| 1281 |
+
DisplayMemberPath="Name" SelectedItem="{Binding SelectedVersion, UpdateSourceTrigger=PropertyChanged}"
|
| 1282 |
+
Margin="5,4" VerticalContentAlignment="Center"/>
|
| 1283 |
+
</DataTemplate>
|
| 1284 |
+
</DataGridTemplateColumn.CellTemplate>
|
| 1285 |
+
</DataGridTemplateColumn>
|
| 1286 |
+
<DataGridTemplateColumn Header="操作" Width="100">
|
| 1287 |
+
<DataGridTemplateColumn.CellTemplate>
|
| 1288 |
+
<DataTemplate>
|
| 1289 |
+
<Button Name="RowDL" Content="下载" Style="{StaticResource PrimaryButton}" Height="28" Padding="15,2"/>
|
| 1290 |
+
</DataTemplate>
|
| 1291 |
+
</DataGridTemplateColumn.CellTemplate>
|
| 1292 |
+
</DataGridTemplateColumn>
|
| 1293 |
+
</DataGrid.Columns>
|
| 1294 |
+
</DataGrid>
|
| 1295 |
+
|
| 1296 |
+
<!-- 加载遮罩 (放在此处确保只覆盖主列表) -->
|
| 1297 |
+
<StackPanel Name="LoadingOverlay" VerticalAlignment="Center" HorizontalAlignment="Center">
|
| 1298 |
+
<ProgressBar IsIndeterminate="True" Width="220" Height="4" Background="{DynamicResource BorderBrush}" Foreground="#0078D4" BorderThickness="0"/>
|
| 1299 |
+
<TextBlock Text="正在同步云端元数据..." Margin="0,15,0,0" HorizontalAlignment="Center" Foreground="{DynamicResource TextSecBrush}" FontSize="13"/>
|
| 1300 |
+
</StackPanel>
|
| 1301 |
+
</Grid>
|
| 1302 |
+
</Border>
|
| 1303 |
+
|
| 1304 |
+
<!-- 分割标题 -->
|
| 1305 |
+
<Grid Name="QueueHeader" Grid.Row="1" Margin="0,15,0,8" Opacity="1">
|
| 1306 |
+
<TextBlock Text="任务队列" Foreground="{DynamicResource TextSecBrush}" FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
| 1307 |
+
<TextBlock Name="StatText" Text="队列统计: 总计 0 | 运行中 0 | 已完成 0" HorizontalAlignment="Right" FontSize="11" Foreground="{DynamicResource TextSecBrush}" VerticalAlignment="Center"/>
|
| 1308 |
+
</Grid>
|
| 1309 |
+
|
| 1310 |
+
<!-- 任务队列表格 -->
|
| 1311 |
+
<Border Name="QueueBorder" Grid.Row="2" Height="140" Background="{DynamicResource PanelBGBrush}" CornerRadius="8" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Opacity="1">
|
| 1312 |
+
<DataGrid Name="QueueGrid" AutoGenerateColumns="False" IsReadOnly="True"
|
| 1313 |
+
HeadersVisibility="Column" GridLinesVisibility="None" Background="Transparent"
|
| 1314 |
+
BorderThickness="0" RowHeight="36"
|
| 1315 |
+
SelectionMode="Single" SelectionUnit="FullRow">
|
| 1316 |
+
<DataGrid.Resources>
|
| 1317 |
+
<Style TargetType="DataGridCell">
|
| 1318 |
+
<Setter Property="Background" Value="Transparent"/>
|
| 1319 |
+
<Setter Property="BorderThickness" Value="0"/>
|
| 1320 |
+
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
| 1321 |
+
<Setter Property="Foreground" Value="{DynamicResource TextMainBrush}"/>
|
| 1322 |
+
<Setter Property="Template">
|
| 1323 |
+
<Setter.Value>
|
| 1324 |
+
<ControlTemplate TargetType="DataGridCell">
|
| 1325 |
+
<Border Background="{TemplateBinding Background}" BorderThickness="0" SnapsToDevicePixels="True">
|
| 1326 |
+
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0"/>
|
| 1327 |
+
</Border>
|
| 1328 |
+
</ControlTemplate>
|
| 1329 |
+
</Setter.Value>
|
| 1330 |
+
</Setter>
|
| 1331 |
+
</Style>
|
| 1332 |
+
<Style TargetType="DataGridColumnHeader">
|
| 1333 |
+
<Setter Property="Background" Value="{DynamicResource PanelBGBrush}"/>
|
| 1334 |
+
<Setter Property="Foreground" Value="{DynamicResource TextSecBrush}"/>
|
| 1335 |
+
<Setter Property="Padding" Value="10,6"/>
|
| 1336 |
+
<Setter Property="FontSize" Value="11"/>
|
| 1337 |
+
<Setter Property="FontWeight" Value="SemiBold"/>
|
| 1338 |
+
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
| 1339 |
+
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
|
| 1340 |
+
</Style>
|
| 1341 |
+
<Style TargetType="DataGridRow">
|
| 1342 |
+
<Setter Property="Background" Value="Transparent"/>
|
| 1343 |
+
<Style.Triggers>
|
| 1344 |
+
<Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="$($colors.ItemHover)"/></Trigger>
|
| 1345 |
+
</Style.Triggers>
|
| 1346 |
+
</Style>
|
| 1347 |
+
</DataGrid.Resources>
|
| 1348 |
+
<DataGrid.Columns>
|
| 1349 |
+
<DataGridTextColumn Header="任务名称" Binding="{Binding Name}" Width="2*"/>
|
| 1350 |
+
<DataGridTextColumn Header="状态" Binding="{Binding Status}" Width="100">
|
| 1351 |
+
<DataGridTextColumn.ElementStyle>
|
| 1352 |
+
<Style TargetType="TextBlock">
|
| 1353 |
+
<Style.Triggers>
|
| 1354 |
+
<Trigger Property="Text" Value="下载中"><Setter Property="Foreground" Value="#0078D4"/><Setter Property="FontWeight" Value="Bold"/></Trigger>
|
| 1355 |
+
<Trigger Property="Text" Value="解压中"><Setter Property="Foreground" Value="#038387"/><Setter Property="FontWeight" Value="Bold"/></Trigger>
|
| 1356 |
+
<Trigger Property="Text" Value="已完成"><Setter Property="Foreground" Value="#107C10"/></Trigger>
|
| 1357 |
+
<Trigger Property="Text" Value="失败"><Setter Property="Foreground" Value="#E81123"/></Trigger>
|
| 1358 |
+
<Trigger Property="Text" Value="已取消"><Setter Property="Foreground" Value="Gray"/></Trigger>
|
| 1359 |
+
</Style.Triggers>
|
| 1360 |
+
</Style>
|
| 1361 |
+
</DataGridTextColumn.ElementStyle>
|
| 1362 |
+
</DataGridTextColumn>
|
| 1363 |
+
<DataGridTextColumn Header="进度" Binding="{Binding Progress}" Width="80"/>
|
| 1364 |
+
<DataGridTemplateColumn Header="操作" Width="80">
|
| 1365 |
+
<DataGridTemplateColumn.CellTemplate>
|
| 1366 |
+
<DataTemplate>
|
| 1367 |
+
<Button Name="KillTask" Content="终止" Height="22" Padding="8,0" FontSize="11"
|
| 1368 |
+
Background="#FFF1F0" Foreground="#E81123" BorderBrush="#FFCCC7">
|
| 1369 |
+
<Button.Style>
|
| 1370 |
+
<Style TargetType="Button">
|
| 1371 |
+
<Style.Triggers>
|
| 1372 |
+
<DataTrigger Binding="{Binding Status}" Value="已完成"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
|
| 1373 |
+
<DataTrigger Binding="{Binding Status}" Value="失败"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
|
| 1374 |
+
<DataTrigger Binding="{Binding Status}" Value="已取消"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
|
| 1375 |
+
</Style.Triggers>
|
| 1376 |
+
</Style>
|
| 1377 |
+
</Button.Style>
|
| 1378 |
+
</Button>
|
| 1379 |
+
</DataTemplate>
|
| 1380 |
+
</DataGridTemplateColumn.CellTemplate>
|
| 1381 |
+
</DataGridTemplateColumn>
|
| 1382 |
+
</DataGrid.Columns>
|
| 1383 |
+
</DataGrid>
|
| 1384 |
+
</Border>
|
| 1385 |
+
</Grid>
|
| 1386 |
+
|
| 1387 |
+
<!-- 底部控制 -->
|
| 1388 |
+
<StackPanel Grid.Row="2" Margin="0,20,0,0">
|
| 1389 |
+
<Grid Margin="0,0,0,8">
|
| 1390 |
+
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
|
| 1391 |
+
<TextBox Name="PathInput" Grid.Column="0" Margin="0,0,10,0"/>
|
| 1392 |
+
<Button Name="BrowseBtn" Grid.Column="1" Content="更改保存路径" Background="{DynamicResource BtnNormalBrush}"/>
|
| 1393 |
+
</Grid>
|
| 1394 |
+
|
| 1395 |
+
<!-- 磁盘空间显示 -->
|
| 1396 |
+
<StackPanel Margin="0,0,0,8">
|
| 1397 |
+
<Grid Margin="2,0">
|
| 1398 |
+
<TextBlock Name="DiskLabel" Text="磁盘空间: 计算中..." FontSize="11" Foreground="{DynamicResource TextSecBrush}"/>
|
| 1399 |
+
<TextBlock Name="DiskPercent" Text="0%" HorizontalAlignment="Right" FontSize="11" Foreground="{DynamicResource TextSecBrush}"/>
|
| 1400 |
+
</Grid>
|
| 1401 |
+
<ProgressBar Name="DiskBar" Height="4" Margin="0,5,0,0" Background="{DynamicResource BorderBrush}" Foreground="#0078D4" BorderThickness="0"/>
|
| 1402 |
+
</StackPanel>
|
| 1403 |
+
|
| 1404 |
+
<!-- 底部队列统计 (隐藏队列时显示) -->
|
| 1405 |
+
<TextBlock Name="StatTextBottom" Text="队列统计: 总计 0 | 运行中 0 | 已完成 0" FontSize="11" Foreground="{DynamicResource TextSecBrush}" Margin="2,0,0,10" Visibility="Collapsed"/>
|
| 1406 |
+
|
| 1407 |
+
<DockPanel>
|
| 1408 |
+
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
|
| 1409 |
+
<CheckBox Name="AutoExtract" Content="下载完成后自动解压到当前目录" IsChecked="True" Style="{StaticResource ToggleSwitchStyle}" Margin="0,0,20,0"/>
|
| 1410 |
+
<CheckBox Name="AutoDelete" Content="解压成功后删除压缩包" IsChecked="True" Style="{StaticResource ToggleSwitchStyle}" />
|
| 1411 |
+
</StackPanel>
|
| 1412 |
+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
| 1413 |
+
<Button Name="RefreshBtn" Content="刷新同步" Style="{StaticResource PrimaryButton}" Width="100" Height="34" Margin="0,0,10,0"/>
|
| 1414 |
+
<Button Name="ToggleQueueBtn" Content="隐藏/展开队列" Width="120" Height="34" Style="{StaticResource PrimaryButton}"/>
|
| 1415 |
+
</StackPanel>
|
| 1416 |
+
</DockPanel>
|
| 1417 |
+
</StackPanel>
|
| 1418 |
+
</Grid>
|
| 1419 |
+
</Grid>
|
| 1420 |
+
</Border>
|
| 1421 |
+
</Window>
|
| 1422 |
+
"@
|
| 1423 |
+
$reader = New-Object System.Xml.XmlNodeReader $xaml
|
| 1424 |
+
$window = [Windows.Markup.XamlReader]::Load($reader)
|
| 1425 |
+
|
| 1426 |
+
$UI = [PSCustomObject]@{
|
| 1427 |
+
Window = $window; MainGrid = $window.FindName("MainGrid"); QueueGrid = $window.FindName("QueueGrid"); UpdateTimeText = $window.FindName("UpdateTime"); PathInput = $window.FindName("PathInput")
|
| 1428 |
+
StableRadio = $window.FindName("Stable"); NightlyRadio = $window.FindName("Nightly"); HFRadio = $window.FindName("HF"); MSRadio = $window.FindName("MS")
|
| 1429 |
+
RefreshBtn = $window.FindName("RefreshBtn"); ToggleQueueBtn = $window.FindName("ToggleQueueBtn"); BrowseBtn = $window.FindName("BrowseBtn")
|
| 1430 |
+
AutoExtract = $window.FindName("AutoExtract"); AutoDelete = $window.FindName("AutoDelete")
|
| 1431 |
+
ProjBtn = $window.FindName("ProjBtn"); DocBtn = $window.FindName("DocBtn"); LauncherBtn = $window.FindName("LauncherBtn"); IssueBtn = $window.FindName("IssueBtn");
|
| 1432 |
+
LoadingOverlay = $window.FindName("LoadingOverlay")
|
| 1433 |
+
TitleBar = $window.FindName("TitleBar"); CloseBtn = $window.FindName("CloseBtn")
|
| 1434 |
+
MinBtn = $window.FindName("MinBtn"); MaxBtn = $window.FindName("MaxBtn")
|
| 1435 |
+
DiskLabel = $window.FindName("DiskLabel"); DiskBar = $window.FindName("DiskBar"); DiskPercent = $window.FindName("DiskPercent")
|
| 1436 |
+
QueueHeader = $window.FindName("QueueHeader"); QueueBorder = $window.FindName("QueueBorder"); StatText = $window.FindName("StatText"); StatTextBottom = $window.FindName("StatTextBottom")
|
| 1437 |
+
DataQueueGrid = $window.FindName("DataQueueGrid")
|
| 1438 |
+
MainBorder = $window.FindName("MainBorder")
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
# 统计更新逻辑
|
| 1442 |
+
$script:UpdateStatistics = {
|
| 1443 |
+
param($UI, $State)
|
| 1444 |
+
|
| 1445 |
+
$queue = $State.TaskQueue
|
| 1446 |
+
if ($null -eq $queue) {
|
| 1447 |
+
$total = 0
|
| 1448 |
+
$running = 0
|
| 1449 |
+
$completed = 0
|
| 1450 |
+
} else {
|
| 1451 |
+
$total = $queue.Count
|
| 1452 |
+
$running = (@($queue | Where-Object { $_.Status -eq "下载中" -or $_.Status -eq "解压中" })).Count
|
| 1453 |
+
$completed = (@($queue | Where-Object { $_.Status -eq "已完成" })).Count
|
| 1454 |
+
}
|
| 1455 |
+
|
| 1456 |
+
$text = "队列统计: 总计 $total | 运行中 $running | 已完成 $completed"
|
| 1457 |
+
$UI.Window.Dispatcher.Invoke([Action]{
|
| 1458 |
+
$UI.StatText.Text = $text
|
| 1459 |
+
$UI.StatTextBottom.Text = $text
|
| 1460 |
+
})
|
| 1461 |
+
}
|
| 1462 |
+
|
| 1463 |
+
# 队列刷新逻辑
|
| 1464 |
+
$script:UpdateQueueUI = {
|
| 1465 |
+
param($UI, $State)
|
| 1466 |
+
$UI.Window.Dispatcher.Invoke([Action]{
|
| 1467 |
+
$UI.QueueGrid.ItemsSource = $null
|
| 1468 |
+
$UI.QueueGrid.ItemsSource = $State.TaskQueue
|
| 1469 |
+
})
|
| 1470 |
+
& $script:UpdateStatistics -UI $UI -State $State
|
| 1471 |
+
}
|
| 1472 |
+
|
| 1473 |
+
# 磁盘空间更新逻辑
|
| 1474 |
+
$script:UpdateDiskInfo = {
|
| 1475 |
+
param($UI)
|
| 1476 |
+
try {
|
| 1477 |
+
$path = $UI.PathInput.Text
|
| 1478 |
+
if (Test-Path $path) {
|
| 1479 |
+
# 路径合法:恢复正常颜色
|
| 1480 |
+
$UI.PathInput.BorderBrush = [System.Windows.Media.Brushes]::DodgerBlue
|
| 1481 |
+
$UI.PathInput.Background = [System.Windows.Media.Brushes]::White
|
| 1482 |
+
|
| 1483 |
+
$drive = Get-PSDrive -PSProvider FileSystem | Where-Object { $path.StartsWith($_.Root) } | Select-Object -First 1
|
| 1484 |
+
if ($null -ne $drive) {
|
| 1485 |
+
$total = $drive.Used + $drive.Free
|
| 1486 |
+
$usedPercent = ($drive.Used / $total) * 100
|
| 1487 |
+
$freeGB = [Math]::Round($drive.Free / 1GB, 2)
|
| 1488 |
+
$totalGB = [Math]::Round($total / 1GB, 2)
|
| 1489 |
+
|
| 1490 |
+
$UI.DiskLabel.Text = "可用空间: $freeGB GB / 总量: $totalGB GB"
|
| 1491 |
+
$UI.DiskBar.Value = $usedPercent
|
| 1492 |
+
$UI.DiskPercent.Text = "$([Math]::Round($usedPercent, 0))%"
|
| 1493 |
+
|
| 1494 |
+
if ($freeGB -lt 10) {
|
| 1495 |
+
$UI.DiskBar.Foreground = [System.Windows.Media.Brushes]::Red
|
| 1496 |
+
Write-Host "[状态] 磁盘空间严重不足: $freeGB GB" -ForegroundColor Red
|
| 1497 |
+
}
|
| 1498 |
+
else { $UI.DiskBar.Foreground = [System.Windows.Media.Brushes]::DodgerBlue }
|
| 1499 |
+
}
|
| 1500 |
+
} else {
|
| 1501 |
+
# 路径非法:变红提示
|
| 1502 |
+
Write-Host "[状态] 当前路径无效: $path" -ForegroundColor Red
|
| 1503 |
+
$UI.PathInput.BorderBrush = [System.Windows.Media.Red]
|
| 1504 |
+
$UI.PathInput.Background = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.Color]::FromArgb(30, 255, 0, 0)) # 浅红背景
|
| 1505 |
+
$UI.DiskLabel.Text = "磁盘空间: 路径无效"
|
| 1506 |
+
$UI.DiskBar.Value = 0
|
| 1507 |
+
$UI.DiskPercent.Text = "ERR"
|
| 1508 |
+
$UI.DiskBar.Foreground = [System.Windows.Media.Brushes]::Gray
|
| 1509 |
+
}
|
| 1510 |
+
} catch {}
|
| 1511 |
+
}
|
| 1512 |
+
|
| 1513 |
+
# 处理无边框窗口逻辑 (手动模拟最大化以解决毛玻璃遮挡任务栏)
|
| 1514 |
+
$script:RestoreBounds = $null
|
| 1515 |
+
|
| 1516 |
+
$UI.TitleBar.Add_MouseLeftButtonDown({
|
| 1517 |
+
if ($null -ne $script:RestoreBounds) {
|
| 1518 |
+
# 如果在最大化状态拖动,先还原
|
| 1519 |
+
$UI.MaxBtn.RaiseEvent((New-Object System.Windows.RoutedEventArgs([System.Windows.Controls.Button]::ClickEvent)))
|
| 1520 |
+
}
|
| 1521 |
+
$window.DragMove()
|
| 1522 |
+
})
|
| 1523 |
+
$UI.TitleBar.Add_MouseLeftButtonDown({ if ($_.ClickCount -eq 2) { $UI.MaxBtn.RaiseEvent((New-Object System.Windows.RoutedEventArgs([System.Windows.Controls.Button]::ClickEvent))) } })
|
| 1524 |
+
|
| 1525 |
+
$UI.MinBtn.Add_Click({ $window.WindowState = "Minimized" })
|
| 1526 |
+
|
| 1527 |
+
$UI.MaxBtn.Add_Click({
|
| 1528 |
+
$handle = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle
|
| 1529 |
+
if ($null -ne $script:RestoreBounds) {
|
| 1530 |
+
# 还原逻辑
|
| 1531 |
+
$window.Left = $script:RestoreBounds.Left
|
| 1532 |
+
$window.Top = $script:RestoreBounds.Top
|
| 1533 |
+
$window.Width = $script:RestoreBounds.Width
|
| 1534 |
+
$window.Height = $script:RestoreBounds.Height
|
| 1535 |
+
$script:RestoreBounds = $null
|
| 1536 |
+
|
| 1537 |
+
$UI.MaxBtn.Content = "⬜"
|
| 1538 |
+
if ($null -ne $UI.MainBorder) { $UI.MainBorder.CornerRadius = 12 }
|
| 1539 |
+
# 开启系统圆角裁切以解决毛玻璃穿透
|
| 1540 |
+
[BlurHelper]::SetRounding($handle, $true)
|
| 1541 |
+
} else {
|
| 1542 |
+
# 手动模拟最大化逻辑 (不使用 WindowState = Maximized)
|
| 1543 |
+
$script:RestoreBounds = [PSCustomObject]@{
|
| 1544 |
+
Left = $window.Left; Top = $window.Top; Width = $window.Width; Height = $window.Height
|
| 1545 |
+
}
|
| 1546 |
+
|
| 1547 |
+
# 获取当前屏幕工作区 (Win32 Pixels)
|
| 1548 |
+
$screen = [System.Windows.Forms.Screen]::FromHandle($handle)
|
| 1549 |
+
$workingArea = $screen.WorkingArea
|
| 1550 |
+
|
| 1551 |
+
# 考虑 DPI 缩放转换
|
| 1552 |
+
$source = [System.Windows.PresentationSource]::FromVisual($window)
|
| 1553 |
+
if ($null -ne $source) {
|
| 1554 |
+
$matrix = $source.CompositionTarget.TransformToDevice
|
| 1555 |
+
$window.Left = $workingArea.X / $matrix.M11
|
| 1556 |
+
$window.Top = $workingArea.Y / $matrix.M22
|
| 1557 |
+
$window.Width = $workingArea.Width / $matrix.M11
|
| 1558 |
+
$window.Height = $workingArea.Height / $matrix.M22
|
| 1559 |
+
}
|
| 1560 |
+
|
| 1561 |
+
$UI.MaxBtn.Content = "❐"
|
| 1562 |
+
if ($null -ne $UI.MainBorder) { $UI.MainBorder.CornerRadius = 0 }
|
| 1563 |
+
# 关闭系统圆角裁切
|
| 1564 |
+
[BlurHelper]::SetRounding($handle, $false)
|
| 1565 |
+
}
|
| 1566 |
+
})
|
| 1567 |
+
$UI.CloseBtn.Add_Click({ $window.Close() })
|
| 1568 |
+
|
| 1569 |
+
$State = [PSCustomObject]@{ Metadata = $null; TaskQueue = @(); CurrentTask = $null; CurrentExtractionTask = $null; IsCancelled = $false }
|
| 1570 |
+
|
| 1571 |
+
$UI.RefreshBtn.Add_Click({ Invoke-Refresh -UI $UI -State $State })
|
| 1572 |
+
$onStateChanged = { & $script:SyncDataGridLogic -UI $UI -State $State }
|
| 1573 |
+
$UI.StableRadio.Add_Checked($onStateChanged); $UI.NightlyRadio.Add_Checked($onStateChanged); $UI.HFRadio.Add_Checked($onStateChanged); $UI.MSRadio.Add_Checked($onStateChanged)
|
| 1574 |
+
|
| 1575 |
+
# 实时验证路径
|
| 1576 |
+
$UI.PathInput.Add_TextChanged({ & $script:UpdateDiskInfo -UI $UI })
|
| 1577 |
+
|
| 1578 |
+
$UI.BrowseBtn.Add_Click({
|
| 1579 |
+
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog;
|
| 1580 |
+
if ($dialog.ShowDialog() -eq "OK") {
|
| 1581 |
+
$UI.PathInput.Text = $dialog.SelectedPath
|
| 1582 |
+
}
|
| 1583 |
+
})
|
| 1584 |
+
$UI.MainGrid.AddHandler([System.Windows.Controls.Button]::ClickEvent, [System.Windows.RoutedEventHandler]{ param($s, $e) if ($e.OriginalSource.Name -eq "RowDL") { Invoke-DownloadAction -UI $UI -State $State -Button $e.OriginalSource } })
|
| 1585 |
+
|
| 1586 |
+
$UI.ProjBtn.Add_Click({ Open-Url -Url "https://github.com/licyk/sd-webui-all-in-one" })
|
| 1587 |
+
$UI.DocBtn.Add_Click({ Open-Url -Url "https://licyk.github.io/sd-webui-all-in-one/portable/portable" })
|
| 1588 |
+
$UI.LauncherBtn.Add_Click({ Open-Url -Url "https://licyk.github.io/sd-webui-all-in-one/tools/launcher-gui" })
|
| 1589 |
+
$UI.IssueBtn.Add_Click({ Open-Url -Url "https://github.com/licyk/sd-webui-all-in-one/issues" })
|
| 1590 |
+
|
| 1591 |
+
# 队列显隐切换 (增加过渡动画)
|
| 1592 |
+
$UI.ToggleQueueBtn.Add_Click({
|
| 1593 |
+
$duration = [TimeSpan]::FromMilliseconds(300)
|
| 1594 |
+
$targetHeight = 140 # 设定的展开高度
|
| 1595 |
+
|
| 1596 |
+
if ($UI.QueueBorder.Visibility -eq "Visible" -and $UI.QueueBorder.Height -gt 0) {
|
| 1597 |
+
# 收起动画
|
| 1598 |
+
$animHeight = New-Object System.Windows.Media.Animation.DoubleAnimation(0, $duration)
|
| 1599 |
+
$animOpacity = New-Object System.Windows.Media.Animation.DoubleAnimation(0, $duration)
|
| 1600 |
+
|
| 1601 |
+
$animHeight.add_Completed({
|
| 1602 |
+
$UI.QueueBorder.Visibility = "Collapsed"
|
| 1603 |
+
$UI.QueueHeader.Visibility = "Collapsed"
|
| 1604 |
+
$UI.StatTextBottom.Visibility = "Visible"
|
| 1605 |
+
})
|
| 1606 |
+
|
| 1607 |
+
$UI.QueueBorder.BeginAnimation([System.Windows.Controls.Border]::HeightProperty, $animHeight)
|
| 1608 |
+
$UI.QueueBorder.BeginAnimation([System.Windows.Controls.Border]::OpacityProperty, $animOpacity)
|
| 1609 |
+
$UI.QueueHeader.BeginAnimation([System.Windows.Controls.Grid]::OpacityProperty, $animOpacity)
|
| 1610 |
+
} else {
|
| 1611 |
+
# 展开动画
|
| 1612 |
+
$UI.QueueBorder.Visibility = "Visible"
|
| 1613 |
+
$UI.QueueHeader.Visibility = "Visible"
|
| 1614 |
+
$UI.StatTextBottom.Visibility = "Collapsed"
|
| 1615 |
+
|
| 1616 |
+
$animHeight = New-Object System.Windows.Media.Animation.DoubleAnimation($targetHeight, $duration)
|
| 1617 |
+
$animOpacity = New-Object System.Windows.Media.Animation.DoubleAnimation(1, $duration)
|
| 1618 |
+
|
| 1619 |
+
$UI.QueueBorder.BeginAnimation([System.Windows.Controls.Border]::HeightProperty, $animHeight)
|
| 1620 |
+
$UI.QueueBorder.BeginAnimation([System.Windows.Controls.Border]::OpacityProperty, $animOpacity)
|
| 1621 |
+
$UI.QueueHeader.BeginAnimation([System.Windows.Controls.Grid]::OpacityProperty, $animOpacity)
|
| 1622 |
+
}
|
| 1623 |
+
})
|
| 1624 |
+
|
| 1625 |
+
# 个体任务终止逻辑
|
| 1626 |
+
$UI.QueueGrid.AddHandler([System.Windows.Controls.Button]::ClickEvent, [System.Windows.RoutedEventHandler]{
|
| 1627 |
+
param($s, $e)
|
| 1628 |
+
if ($e.OriginalSource.Name -eq "KillTask") {
|
| 1629 |
+
$task = $e.OriginalSource.DataContext
|
| 1630 |
+
Write-Host "[UI] 用户请求终止任务: $($task.Name)" -ForegroundColor Yellow
|
| 1631 |
+
|
| 1632 |
+
# 如果是当前运行的下载任务
|
| 1633 |
+
if ($null -ne $State.CurrentTask -and $State.CurrentTask.QueueTask -eq $task) {
|
| 1634 |
+
$task.Status = "已取消"
|
| 1635 |
+
try { Stop-Process -Id $State.CurrentTask.Process.Id -Force -ErrorAction SilentlyContinue } catch {}
|
| 1636 |
+
}
|
| 1637 |
+
# 如果是当前运行的解压任务
|
| 1638 |
+
elseif ($null -ne $State.CurrentExtractionTask -and $State.CurrentExtractionTask.QueueTask -eq $task) {
|
| 1639 |
+
$task.Status = "已取消"
|
| 1640 |
+
try { Stop-Process -Id $State.CurrentExtractionTask.Process.Id -Force -ErrorAction SilentlyContinue } catch {}
|
| 1641 |
+
}
|
| 1642 |
+
# 等待中任务
|
| 1643 |
+
else {
|
| 1644 |
+
$task.Status = "已取消"
|
| 1645 |
+
}
|
| 1646 |
+
& $script:UpdateQueueUI -UI $UI -State $State
|
| 1647 |
+
}
|
| 1648 |
+
})
|
| 1649 |
+
|
| 1650 |
+
# 定时器:主调度器与磁盘空间刷新
|
| 1651 |
+
$script:tickCounter = 0
|
| 1652 |
+
$timer = New-Object System.Windows.Threading.DispatcherTimer
|
| 1653 |
+
$timer.Interval = [TimeSpan]::FromMilliseconds(100)
|
| 1654 |
+
$timer.Add_Tick({
|
| 1655 |
+
# 1. 磁盘空间刷新 (1s)
|
| 1656 |
+
$script:tickCounter++
|
| 1657 |
+
if ($script:tickCounter -ge 10) {
|
| 1658 |
+
$script:tickCounter = 0
|
| 1659 |
+
& $script:UpdateDiskInfo -UI $UI
|
| 1660 |
+
}
|
| 1661 |
+
|
| 1662 |
+
# 2. 任务调度逻辑
|
| 1663 |
+
$isAnyBusy = ($null -ne $State.CurrentTask -or $null -ne $State.CurrentExtractionTask)
|
| 1664 |
+
if (-not $isAnyBusy) {
|
| 1665 |
+
# ��找队列中第一个等待中的任务
|
| 1666 |
+
$nextTask = $State.TaskQueue | Where-Object { $_.Status -eq "等待中" } | Select-Object -First 1
|
| 1667 |
+
if ($null -ne $nextTask) {
|
| 1668 |
+
$nextTask.Status = "下载中"
|
| 1669 |
+
& $script:UpdateQueueUI -UI $UI -State $State
|
| 1670 |
+
Invoke-DownloadTask -Url $nextTask.Url -OutDir $nextTask.OutDir -SaveName $nextTask.Name -State $State -QueueTask $nextTask | Out-Null
|
| 1671 |
+
}
|
| 1672 |
+
}
|
| 1673 |
+
|
| 1674 |
+
# 3. 下载进程监控
|
| 1675 |
+
if ($null -ne $State.CurrentTask) {
|
| 1676 |
+
$task = $State.CurrentTask
|
| 1677 |
+
if ($task.Process.HasExited) {
|
| 1678 |
+
$exitCode = $task.Process.ExitCode
|
| 1679 |
+
Write-Host "[任务] 下载进程退出 [代码: $exitCode]" -ForegroundColor Blue
|
| 1680 |
+
|
| 1681 |
+
if ($task.QueueTask.Status -ne "已取消") {
|
| 1682 |
+
if ($exitCode -eq 0) {
|
| 1683 |
+
if ($task.QueueTask.AutoExtract) {
|
| 1684 |
+
$task.QueueTask.Progress = "50%"
|
| 1685 |
+
$task.QueueTask.Status = "解压中"
|
| 1686 |
+
$fullPath = Join-Path $task.OutDir $task.SaveName
|
| 1687 |
+
Write-Host "[后处理] 下载完成,开始解压: $fullPath" -ForegroundColor Cyan
|
| 1688 |
+
Show-Async-MsgBox -Message "下载已完成,正在后台启动解压...`n`n源文件:`n$fullPath`n`n解压目录:`n$($task.OutDir)" -Title "下载完成"
|
| 1689 |
+
Start-ExtractionTask -FilePath $fullPath -ExtractDir $task.OutDir -State $State -QueueTask $task.QueueTask | Out-Null
|
| 1690 |
+
} else {
|
| 1691 |
+
$task.QueueTask.Progress = "100%"
|
| 1692 |
+
$task.QueueTask.Status = "已完成"
|
| 1693 |
+
Show-Async-MsgBox -Message "任务已全部完成!`n`n保存文件:`n$($task.SaveName)`n`n保存路径:`n$($task.OutDir)" -Title "任务完成"
|
| 1694 |
+
}
|
| 1695 |
+
} else {
|
| 1696 |
+
$task.QueueTask.Status = "失败"
|
| 1697 |
+
Show-Async-MsgBox -Message "任务下载失败,请检查控制台日志。`n退出码: ${exitCode}`n`n报告问题与寻求帮助请前往: `nhttps://github.com/licyk/sd-webui-all-in-one/issues" -Title "下载失败" -Icon "Error"
|
| 1698 |
+
}
|
| 1699 |
+
}
|
| 1700 |
+
$State.CurrentTask = $null
|
| 1701 |
+
& $script:UpdateQueueUI -UI $UI -State $State
|
| 1702 |
+
}
|
| 1703 |
+
}
|
| 1704 |
+
|
| 1705 |
+
# 4. 解压进程监控
|
| 1706 |
+
if ($null -ne $State.CurrentExtractionTask) {
|
| 1707 |
+
$task = $State.CurrentExtractionTask
|
| 1708 |
+
if ($task.Process.HasExited) {
|
| 1709 |
+
$exitCode = $task.Process.ExitCode
|
| 1710 |
+
Write-Host "[后处理] 解压进程退出 [代码: $exitCode]" -ForegroundColor Blue
|
| 1711 |
+
|
| 1712 |
+
if ($task.QueueTask.Status -ne "已取消") {
|
| 1713 |
+
if ($exitCode -eq 0) {
|
| 1714 |
+
$task.QueueTask.Status = "已完成"
|
| 1715 |
+
$task.QueueTask.Progress = "100%"
|
| 1716 |
+
if ($task.QueueTask.AutoDelete) {
|
| 1717 |
+
try {
|
| 1718 |
+
Remove-Item $task.FilePath -Force
|
| 1719 |
+
Write-Host "[后处理] 已删除压缩包: $($task.FilePath)" -ForegroundColor Gray
|
| 1720 |
+
} catch {
|
| 1721 |
+
Write-Host "[后处理] 删除压缩包失败: $($_.Exception.Message)" -ForegroundColor Red
|
| 1722 |
+
}
|
| 1723 |
+
}
|
| 1724 |
+
Show-Async-MsgBox -Message "解压已成功完成!`n`n源文件:`n$($task.FilePath)`n`n解压至:`n$($task.ExtractDir)" -Title "解压成功"
|
| 1725 |
+
} else {
|
| 1726 |
+
$task.QueueTask.Status = "失败"
|
| 1727 |
+
Show-Async-MsgBox -Message "解压过程中出现错误,请检查控制台日志。`n退出码: ${exitCode}`n`n报告问题与寻求帮助请前往: `nhttps://github.com/licyk/sd-webui-all-in-one/issues" -Title "解压失败" -Icon "Error"
|
| 1728 |
+
}
|
| 1729 |
+
}
|
| 1730 |
+
$State.CurrentExtractionTask = $null
|
| 1731 |
+
& $script:UpdateQueueUI -UI $UI -State $State
|
| 1732 |
+
}
|
| 1733 |
+
}
|
| 1734 |
+
})
|
| 1735 |
+
$timer.Start()
|
| 1736 |
+
|
| 1737 |
+
$UI.PathInput.Text = if ($Env:BAT_SCRIPT_ROOT) {
|
| 1738 |
+
$Env:BAT_SCRIPT_ROOT
|
| 1739 |
+
} elseif ($script:ScriptRootPath) {
|
| 1740 |
+
$script:ScriptRootPath
|
| 1741 |
+
} elseif ($env:SCRIPT_ROOT_PATH) {
|
| 1742 |
+
$env:SCRIPT_ROOT_PATH
|
| 1743 |
+
} else {
|
| 1744 |
+
(Get-Location).Path
|
| 1745 |
+
}
|
| 1746 |
+
$window.Add_Loaded({
|
| 1747 |
+
# 获取窗口句柄
|
| 1748 |
+
$handle = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle
|
| 1749 |
+
|
| 1750 |
+
# 开启毛玻璃效果
|
| 1751 |
+
[BlurHelper]::EnableBlur($handle)
|
| 1752 |
+
|
| 1753 |
+
# 应用系统圆角偏好 (解决毛玻璃穿透圆角问题)
|
| 1754 |
+
[BlurHelper]::SetRounding($handle, $true)
|
| 1755 |
+
|
| 1756 |
+
# 设置 DWM 沉浸式深色模式
|
| 1757 |
+
[BlurHelper]::SetDarkMode($handle, $isDarkMode)
|
| 1758 |
+
|
| 1759 |
+
& $script:UpdateDiskInfo -UI $UI
|
| 1760 |
+
Invoke-Refresh -UI $UI -State $State
|
| 1761 |
+
Invoke-Update-Async -IsDarkMode $isDarkMode
|
| 1762 |
+
})
|
| 1763 |
+
$window.ShowDialog() | Out-Null
|
| 1764 |
+
$timer.Stop()
|
| 1765 |
+
Write-Host "[APP] 退出应用" -ForegroundColor Yellow
|
| 1766 |
+
}
|
| 1767 |
+
|
| 1768 |
+
Start-App
|
| 1769 |
+
|
| 1770 |
+
:__PowerShellCode_pxwOLOkD__:
|
| 1771 |
+
|
| 1772 |
+
|
| 1773 |
+
:ExitCode
|
| 1774 |
+
exit /b %_psh_exit_code_%
|