# ============================================================================ # Отправка модели на 3d.furnit.market # # Что делает: берёт папку с сырьём, которое записал скрипт в Базисе, # отправляет её на сайт, сервер собирает модель и возвращает короткую # ссылку для заказчика. Ссылка сразу копируется в буфер обмена. # # Запускать не отсюда, а двойным щелчком по Otpravit.bat рядом. # # ВАЖНО про кодировку. Имена переменных здесь только латиницей, а русский # текст живёт исключительно внутри кавычек. Так сделано нарочно: если файл # случайно пересохранят в другой кодировке, испортятся лишь надписи, а сам # скрипт продолжит работать. С русскими именами переменных он бы просто # перестал запускаться. # ============================================================================ param( [string]$Raw = 'C:\KK_LOCAL\raw', [string]$Title = '', [string]$Order = '', [switch]$Quiet # без итогового окна: для проверки и автоматизации ) $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $Site = 'https://view.furnit.market' $Token = 'f894463bd5cc6f3ed946691aabb54396e3954fe29777c61f' $Api = "$Site/cabinet/upload" $Chunk = 1MB # сервер принимает до 8 МБ за раз, мегабайт — с запасом Add-Type -AssemblyName System.Windows.Forms function Fail([string]$Text) { Write-Host '' Write-Host $Text -ForegroundColor Red try { [System.Windows.Forms.MessageBox]::Show($Text, 'Отправка модели — не получилось') | Out-Null } catch {} exit 1 } function Send-Kk([string]$Url, [byte[]]$Body) { $req = [Net.HttpWebRequest]::Create($Url) $req.Method = 'POST' $req.Timeout = 900000 $req.ReadWriteTimeout = 900000 $req.ContentType = 'application/octet-stream' $req.Headers.Add('X-KK-Token', $Token) if ($null -eq $Body) { $Body = New-Object byte[] 0 } $req.ContentLength = $Body.Length $rs = $req.GetRequestStream(); $rs.Write($Body, 0, $Body.Length); $rs.Close() try { $resp = $req.GetResponse() $sr = New-Object IO.StreamReader($resp.GetResponseStream(), [Text.Encoding]::UTF8) $txt = $sr.ReadToEnd(); $sr.Close(); $resp.Close() return ($txt | ConvertFrom-Json) } catch [Net.WebException] { $we = $_.Exception if ($null -ne $we.Response) { $sr = New-Object IO.StreamReader($we.Response.GetResponseStream(), [Text.Encoding]::UTF8) $txt = $sr.ReadToEnd(); $sr.Close() $j = $null try { $j = $txt | ConvertFrom-Json } catch {} if ($null -ne $j -and $j.error) { Fail ("Сервер ответил: " + $j.error) } Fail ("Сервер ответил ошибкой: " + $txt) } Fail ("Нет связи с сайтом. Проверьте интернет." + [Environment]::NewLine + $we.Message) } } # ------------------------------------------------------------------ проверки Write-Host '' Write-Host ' Отправка модели на сайт' -ForegroundColor Cyan Write-Host ' -----------------------' if ($Token -like '*TOKEN*') { Fail 'В этом файле не подставлен ключ. Скачайте его заново из кабинета.' } if (-not (Test-Path -LiteralPath $Raw)) { Fail ("Папка с сырьём не найдена:" + [Environment]::NewLine + $Raw + [Environment]::NewLine + [Environment]::NewLine + "Сначала запустите скрипт выгрузки в Базисе.") } $mask = '^(tri_\d{1,6}\.kkr|man_\d{1,6}\.kkm|manifest\.json|sborka\.json|final\.json|textures\.txt)$' $files = @(Get-ChildItem -LiteralPath $Raw -File | Where-Object { $_.Name -match $mask }) if ($files.Count -eq 0) { Fail ("В папке нет файлов модели:" + [Environment]::NewLine + $Raw + [Environment]::NewLine + [Environment]::NewLine + "Запустите выгрузку в Базисе заново.") } $total = [int64](($files | Measure-Object -Property Length -Sum).Sum) Write-Host (" Файлов: {0}, объём: {1:N1} МБ" -f $files.Count, ($total / 1MB)) if ([string]::IsNullOrWhiteSpace($Title)) { $Title = Read-Host ' Название модели (Enter - Модель)' if ([string]::IsNullOrWhiteSpace($Title)) { $Title = 'Модель' } } # ------------------------------------------------------------------ отправка # Вес сырья говорим сайту заранее - это прикидка сверху, готовая модель в разы # легче. Сайт откажет по ней только там, где места не хватит наверняка, зато # откажет сейчас, а не через полчаса передачи. $qt = [Uri]::EscapeDataString($Title) $qo = [Uri]::EscapeDataString($Order) $ini = Send-Kk "$Api`?a=init&title=$qt&order=$qo&bytes=$total" $null if (-not $ini.ok) { Fail ("Не удалось начать: " + $ini.error) } $sid = $ini.sid $sent = 0 foreach ($f in $files) { $fs = [IO.File]::OpenRead($f.FullName) try { $buf = New-Object byte[] $Chunk while (($n = $fs.Read($buf, 0, $buf.Length)) -gt 0) { $part = New-Object byte[] $n [Array]::Copy($buf, 0, $part, 0, $n) $r = Send-Kk "$Api`?a=chunk&sid=$sid&name=$($f.Name)" $part if (-not $r.ok) { Fail ("Ошибка передачи: " + $r.error) } $sent += $n $pct = [int](100 * $sent / $total) Write-Progress -Activity 'Отправка модели' -Status "$pct% - $($f.Name)" -PercentComplete $pct } } finally { $fs.Close() } } Write-Progress -Activity 'Отправка модели' -Completed # ------------------------------------------------------------------- сборка Write-Host ' Сервер собирает модель, это занимает несколько секунд...' $fin = Send-Kk "$Api`?a=done&sid=$sid" $null if (-not $fin.ok) { Fail ("Сборка не удалась: " + $fin.error) } $link = $fin.url $st = $fin.stat Write-Host '' Write-Host (" Готово: {0} деталей, {1:N0} треугольников, {2:N1} МБ, {3} с" -f ` $st.nodes, $st.triangles, ($st.bytes / 1MB), $st.seconds) -ForegroundColor Green Write-Host '' Write-Host ' Ссылка для заказчика:' -ForegroundColor Cyan Write-Host " $link" Write-Host '' try { Set-Clipboard -Value $link; Write-Host ' (ссылка скопирована в буфер обмена)' } catch {} if ($Quiet) { exit 0 } # ------------------------------------------------------------ итоговое окно $form = New-Object System.Windows.Forms.Form $form.Text = 'Модель загружена' $form.Size = New-Object System.Drawing.Size(560, 210) $form.StartPosition = 'CenterScreen' $form.FormBorderStyle = 'FixedDialog' $form.MaximizeBox = $false; $form.MinimizeBox = $false; $form.TopMost = $true $lab = New-Object System.Windows.Forms.Label $lab.Text = "Модель загружена. Ссылка для заказчика:" $lab.Location = New-Object System.Drawing.Point(15, 15) $lab.Size = New-Object System.Drawing.Size(510, 20) $form.Controls.Add($lab) $box = New-Object System.Windows.Forms.TextBox $box.Text = $link; $box.ReadOnly = $true $box.Location = New-Object System.Drawing.Point(15, 42) $box.Size = New-Object System.Drawing.Size(515, 24) $form.Controls.Add($box) $btnCopy = New-Object System.Windows.Forms.Button $btnCopy.Text = 'Скопировать' $btnCopy.Location = New-Object System.Drawing.Point(15, 80) $btnCopy.Size = New-Object System.Drawing.Size(150, 32) $btnCopy.Add_Click({ try { [System.Windows.Forms.Clipboard]::SetText($link); $btnCopy.Text = 'Скопировано' } catch {} }) $form.Controls.Add($btnCopy) $btnOpen = New-Object System.Windows.Forms.Button $btnOpen.Text = 'Открыть модель' $btnOpen.Location = New-Object System.Drawing.Point(175, 80) $btnOpen.Size = New-Object System.Drawing.Size(170, 32) $btnOpen.Add_Click({ try { Start-Process $link } catch {} }) $form.Controls.Add($btnOpen) $btnClose = New-Object System.Windows.Forms.Button $btnClose.Text = 'Закрыть' $btnClose.Location = New-Object System.Drawing.Point(430, 80) $btnClose.Size = New-Object System.Drawing.Size(100, 32) $btnClose.DialogResult = [System.Windows.Forms.DialogResult]::OK $form.Controls.Add($btnClose) $form.AcceptButton = $btnClose $form.ShowDialog() | Out-Null