Upload PonyXL.ps1
Browse files- PonyXL.ps1 +206 -0
PonyXL.ps1
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
param (
|
| 2 |
+
[Parameter(Mandatory=$true)]
|
| 3 |
+
[string]$outputFilePath,
|
| 4 |
+
[string]$rentryUrl = "https://rentry.org/ponyxl_loras_n_stuff"
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
function Get-RemoteFileSize {
|
| 8 |
+
param (
|
| 9 |
+
[string]$url
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
try {
|
| 13 |
+
$ProgressPreference = 'SilentlyContinue'
|
| 14 |
+
$response = Invoke-WebRequest -Uri $url -Method Head
|
| 15 |
+
$ProgressPreference = 'Continue'
|
| 16 |
+
$fileSize = [int]$response.Headers['Content-Length']
|
| 17 |
+
return $fileSize;
|
| 18 |
+
} catch {
|
| 19 |
+
Write-Error "Failed to retrieve file size. $_"
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
function Create-Entry {
|
| 24 |
+
param (
|
| 25 |
+
[string]$name,
|
| 26 |
+
[string]$url,
|
| 27 |
+
[int]$size,
|
| 28 |
+
[string] $comment,
|
| 29 |
+
[int]$status
|
| 30 |
+
)
|
| 31 |
+
return [pscustomobject]@{Name=$name;Url=$url;Size=$size;Status=$status;Comment=$comment;Selected=0}
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
Clear-Host
|
| 35 |
+
|
| 36 |
+
#The path to the history file
|
| 37 |
+
$historyFile = Join-Path $outputFilePath "_history.txt"
|
| 38 |
+
|
| 39 |
+
# Initialize an empty dictionary for tracking the history
|
| 40 |
+
$historyDict = @{}
|
| 41 |
+
|
| 42 |
+
if (Test-Path $historyFile -PathType Leaf) {
|
| 43 |
+
# Read the file line by line and populate the dictionary
|
| 44 |
+
Get-Content -Path $historyFile | ForEach-Object {
|
| 45 |
+
# Split the line into key and value based on whitespace
|
| 46 |
+
$lineParts = $_ -split '\s+'
|
| 47 |
+
|
| 48 |
+
# Ensure there are at least two parts on the line
|
| 49 |
+
if ($lineParts.Length -ge 2) {
|
| 50 |
+
$key = $lineParts[0]
|
| 51 |
+
|
| 52 |
+
#$index = $_.IndexOf($lineParts[1])
|
| 53 |
+
#$comment = $_.Substring($index + $lineParts[1].Length).Trim()
|
| 54 |
+
#$value = @($lineParts[1], $comment)
|
| 55 |
+
|
| 56 |
+
# Add an entry to the dictionary
|
| 57 |
+
$historyDict[$key] = $lineParts[1]
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
Write-Host "Getting details about the Loras"
|
| 63 |
+
|
| 64 |
+
# Define a regular expression pattern to match URLs within <a> HTML elements ending with "safetensors"
|
| 65 |
+
$downloadLinkPattern = '<a [^>]*href=["'']?(https?://(?:www\.)?[^\s]+safetensors\b[^"''\s>]*)[^>]*>([^<]*)</a>([^<]*)'
|
| 66 |
+
|
| 67 |
+
# Download the content of the rentry.org page
|
| 68 |
+
$pageContent = Invoke-RestMethod -Uri $rentryUrl
|
| 69 |
+
|
| 70 |
+
# Find all matches using the regular expression pattern
|
| 71 |
+
$matches = $pageContent | Select-String -Pattern $downloadLinkPattern -AllMatches | ForEach-Object { $_.Matches }
|
| 72 |
+
|
| 73 |
+
# Initialize an empty list for tracking the available loras, each entry is a tuple with name, url, file size, integer indicating status (0 is new, 1 is updated), and integer indicating if user wants to download
|
| 74 |
+
$availableLoras = @()
|
| 75 |
+
|
| 76 |
+
# Process each match and extract download link, the first string inside <a> element, and the HTML content after </a>
|
| 77 |
+
foreach ($match in $matches) {
|
| 78 |
+
$url = $match.Groups[1].Value
|
| 79 |
+
$htmlAfterA = $match.Groups[3].Value
|
| 80 |
+
|
| 81 |
+
# Trim the string and split it into two variables
|
| 82 |
+
$trimmedString = $htmlAfterA.Trim()
|
| 83 |
+
$firstWord = $trimmedString.Split(' ', 2)[0]
|
| 84 |
+
$remainingText = $trimmedString.Split(' ', 2)[1]
|
| 85 |
+
|
| 86 |
+
$outputFileWithPath = Join-Path "$outputFilePath" "ponyxl_$firstWord.safetensors"
|
| 87 |
+
|
| 88 |
+
$comment = if($remainingText -ne $null) { $remainingText.Trim() } else { "" }
|
| 89 |
+
|
| 90 |
+
if($historyDict[$firstWord] -eq $null -or -Not (Test-Path $outputFileWithPath -PathType Leaf)) {
|
| 91 |
+
$fileSize = Get-RemoteFileSize -url $url
|
| 92 |
+
|
| 93 |
+
#We don't have the lora yet, add it to the list of options
|
| 94 |
+
$availableLoras += Create-Entry -name $firstWord -url $url -size $fileSize -comment $comment -status 0
|
| 95 |
+
} elseif(-Not ($historyDict[$firstWord] -eq $url)) {
|
| 96 |
+
#The Lora was updated
|
| 97 |
+
$fileSize = Get-RemoteFileSize -url $url
|
| 98 |
+
|
| 99 |
+
$availableLoras += Create-Entry -name $firstWord -url $url -size $fileSize -comment $comment -status 1
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
if($availableLoras.Count -eq 0) {
|
| 105 |
+
Write-Host "There are no available updates based on your history file."
|
| 106 |
+
Exit
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
Do {
|
| 110 |
+
Clear-Host
|
| 111 |
+
Write-Host "Below are the available LoRAs, asterisk indicates a LoRA is queued."
|
| 112 |
+
$index = 1
|
| 113 |
+
foreach ($availableLora in $availableLoras) {
|
| 114 |
+
$name = $availableLora.Name
|
| 115 |
+
$queued = if ($availableLora.Selected -eq 1) { "(*)" } else { "" }
|
| 116 |
+
$size = $availableLora.Size / 1048576
|
| 117 |
+
$status = if ($availableLora.Status -eq 0) { "New" } else { "Updated" }
|
| 118 |
+
|
| 119 |
+
$size = $size.ToString("F2")
|
| 120 |
+
|
| 121 |
+
Write-Host "[$index]) $queued $name - $status ($size MB)"
|
| 122 |
+
$index++;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
Write-Host "Enter a number to toggle queued status, a to queue all, or c to continue to download the queued LoRAs."
|
| 126 |
+
$input = Read-Host "Command"
|
| 127 |
+
|
| 128 |
+
if($input -eq "a") {
|
| 129 |
+
foreach ($availableLora in $availableLoras) {
|
| 130 |
+
$availableLora.Selected = 1
|
| 131 |
+
}
|
| 132 |
+
} elseif($input -ne "c") {
|
| 133 |
+
$parsedInt = -1
|
| 134 |
+
$success = [int]::TryParse($input, [ref]$parsedInt)
|
| 135 |
+
if($success) {
|
| 136 |
+
$availableLoras[$parsedInt - 1].Selected = ($availableLoras[$parsedInt - 1].Selected + 1) % 2
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
} while($input -ne "c")
|
| 140 |
+
|
| 141 |
+
if (-not (Test-Path $outputFilePath -PathType Container)) {
|
| 142 |
+
# If the folder doesn't exist, create it
|
| 143 |
+
New-Item -Path $outputFilePath -ItemType Directory
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
#Download the selected LoRAs
|
| 147 |
+
$downloadedLoras = 0
|
| 148 |
+
foreach ($availableLora in $availableLoras) {
|
| 149 |
+
if($availableLora.Selected -eq 1) {
|
| 150 |
+
try {
|
| 151 |
+
$name = $availableLora.Name
|
| 152 |
+
$url = $availableLora.Url.Trim()
|
| 153 |
+
$comment = $availableLora.Comment
|
| 154 |
+
$outputFileWithPath = Join-Path $outputFilePath "ponyxl_$name.safetensors"
|
| 155 |
+
|
| 156 |
+
#if the file already exists, rename the old one before downloading the new one
|
| 157 |
+
if (Test-Path $outputFileWithPath) {
|
| 158 |
+
$counter = 1
|
| 159 |
+
$newName = "ponyxl_$($name)_old_$counter.safetensors"
|
| 160 |
+
$newPath = Join-Path $outputFilePath $newName
|
| 161 |
+
|
| 162 |
+
while (Test-Path $newPath) {
|
| 163 |
+
$newName = "ponyxl_$($name)_old_$counter.safetensors"
|
| 164 |
+
$newPath = Join-Path $outputFilePath $newName
|
| 165 |
+
$counter++
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
Rename-Item -LiteralPath $outputFileWithPath -NewName $newName
|
| 169 |
+
Write-Host "Renamed old $name LoRA to: $newName"
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
Write-Host "Downloading lora $name with url $url"
|
| 173 |
+
Invoke-WebRequest $url -OutFile $outputFileWithPath
|
| 174 |
+
|
| 175 |
+
++$downloadedLoras
|
| 176 |
+
$historyDict[$name] = $url
|
| 177 |
+
|
| 178 |
+
if ($comment -ne $null -and $comment -ne "") {
|
| 179 |
+
$commentFileWithPath = Join-Path "$outputFilePath" "ponyxl_$name.txt"
|
| 180 |
+
# Clear the existing content of the output text file
|
| 181 |
+
if (Test-Path $commentFileWithPath -PathType Leaf) {
|
| 182 |
+
Clear-Content -Path $commentFileWithPath
|
| 183 |
+
|
| 184 |
+
# Write the new string to the text file
|
| 185 |
+
Add-Content -Path $commentFileWithPath -Value $comment
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
} catch {
|
| 189 |
+
Write-Host "Error downloading lora $name, skipping, error was $_"
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
if (Test-Path $historyFile -PathType Leaf) {
|
| 195 |
+
# Clear the existing content of the history file
|
| 196 |
+
Clear-Content -Path $historyFile
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
#Write the new history
|
| 200 |
+
foreach ($entry in $historyDict.GetEnumerator()) {
|
| 201 |
+
$entryString = "{0} {1}" -f $entry.Key, $entry.Value
|
| 202 |
+
Add-Content -Path $historyFile -Value $entryString
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
Write-Host "Downloaded $downloadedLoras LoRAs."
|
| 206 |
+
Write-Host "Wrote the most recent urls for each model to _history.txt."
|