# ============================================================ # Vidancee Desktop - one-command installer / updater (Windows 10 / 11) # # Open PowerShell and run: # iwr -useb https://vidancee.com/install.ps1 | iex # # - Installs for the current user in %LOCALAPPDATA%\Programs\Vidancee Desktop. # It asks Windows for admin permission (UAC) ONLY to add a Defender # exclusion for that folder; saying No still installs. # - Always installs the LATEST build: the download link is read from # https://vidancee.com/downloads/desktop.json, the same file the app's own # update check uses. # - Re-running it updates in place; the API key, settings and history live in # %APPDATA%\VidanceeDesktop and are never touched. # ============================================================ $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" # PS 5.1 progress bars throttle downloads 10-40x try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 } catch { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } # -- Config ------------------------------------------------- $AppName = "Vidancee Desktop" $ExeName = "VidanceeDesktop.exe" $ProcName = "VidanceeDesktop" $Port = 7790 $InstallDir = Join-Path $env:LOCALAPPDATA "Programs\Vidancee Desktop" $ManifestURL = "https://vidancee.com/downloads/desktop.json" $DesktopLink = Join-Path ([Environment]::GetFolderPath("Desktop")) "Vidancee Desktop.lnk" $StartMenuDir = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs" $StartMenuLink = Join-Path $StartMenuDir "Vidancee Desktop.lnk" $StageRoot = Join-Path $env:TEMP ("vidancee_install_" + [guid]::NewGuid().ToString('N').Substring(0, 8)) $ZipFile = Join-Path $StageRoot "VidanceeDesktop.zip" $StageExtract = Join-Path $StageRoot "extracted" # -- UI helpers --------------------------------------------- $script:step = 1 function Write-Step { param($msg) Write-Host "`n [$script:step] $msg" -ForegroundColor Cyan; $script:step++ } function Write-OK { param($msg) Write-Host " [OK] $msg" -ForegroundColor Green } function Write-Skip { param($msg) Write-Host " [SKIP] $msg" -ForegroundColor Yellow } function Write-Err { param($msg) Write-Host " [FAIL] $msg" -ForegroundColor Red } # A cleanup that can never crash the installer (an AV-quarantined file throws # argument-binding errors that -ErrorAction alone does not stop). function Safe-RemoveItem { param($Path) if (-not $Path) { return } try { if (Test-Path -LiteralPath $Path -ErrorAction SilentlyContinue) { Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop } } catch { Write-Skip "Cleanup of '$Path' failed (not a problem): $($_.Exception.Message)" } } function Get-CurlExe { $c = Join-Path $env:SystemRoot "System32\curl.exe" if (Test-Path $c) { return $c } $cmd = Get-Command curl.exe -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } return $null } function Download-File { param($Url, $OutFile) Safe-RemoveItem $OutFile $curl = Get-CurlExe if ($curl) { # A connection can stall at 0 bytes or crawl (seen 2026-10-26): give up on it # after 20 s under 100 KB/s and RESUME the same file on a fresh connection # (-C -), up to 6 times. A new connection is almost always fast again. for ($try = 1; $try -le 6; $try++) { & $curl -L --fail -C - --connect-timeout 30 --speed-limit 102400 --speed-time 20 --progress-bar -o $OutFile $Url if ($LASTEXITCODE -eq 0 -and (Test-Path $OutFile) -and ((Get-Item $OutFile).Length -gt 0)) { return } if ($LASTEXITCODE -eq 22) { break } # HTTP error (e.g. 404) - retrying won't help Write-Skip "Download slowed down or stopped - resuming (try $($try + 1) of 6)..." Start-Sleep -Seconds 2 } Write-Skip "curl failed (exit $LASTEXITCODE) - trying the built-in downloader" Safe-RemoveItem $OutFile } Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing -TimeoutSec 1800 } function Stop-App { Get-Process -Name $ProcName -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue try { Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop | Select-Object -ExpandProperty OwningProcess -Unique | Where-Object { $_ -and $_ -ne 0 } | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue } } catch { } Start-Sleep -Seconds 1 } $IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator") $IsUpdate = Test-Path (Join-Path $InstallDir $ExeName) Clear-Host Write-Host "" Write-Host " ============================================" -ForegroundColor DarkYellow if ($IsUpdate) { Write-Host " Vidancee Desktop - updating " -ForegroundColor White } else { Write-Host " Vidancee Desktop - installing " -ForegroundColor White } Write-Host " ============================================" -ForegroundColor DarkYellow # -- 1. Latest version --------------------------------------- Write-Step "Checking the latest version..." try { $m = Invoke-RestMethod -Uri $ManifestURL -UseBasicParsing -TimeoutSec 30 $DownloadURL = [string]$m.url if (-not $DownloadURL -or $DownloadURL -notlike "https://vidancee.com/*.zip") { $DownloadURL = "https://vidancee.com/downloads/" + [string]$m.file } Write-OK ("Version " + $m.version + " (" + $m.size_mb + " MB)") } catch { Write-Err "Could not reach vidancee.com. Check your internet connection and try again." return } # Defender exclusions BEFORE extracting, so files can't be quarantined mid-way. # Only this step needs admin. When PowerShell isn't elevated we ask Windows for # permission (UAC "Yes") for JUST these two lines — the install itself stays in # this window, so it lands in THIS user's folders and the app opens as this user # (never as admin). Saying "No" just skips the exclusion; the install continues. Write-Step "Adding a Windows Defender exclusion (so antivirus doesn't block the app)..." $exclCmd = "try { Add-MpPreference -ExclusionPath '$InstallDir','$StageRoot' -ErrorAction Stop; exit 0 } catch { exit 1 }" if ($env:VIDANCEE_FROM_APP -eq "1" -and $IsUpdate -and -not $IsAdmin) { # The app's own "Update now" button: the exclusion was offered at install time — # don't pop a Windows permission box on every update. Write-Skip "Update from the app - keeping the existing exclusion" } elseif ($IsAdmin) { try { Add-MpPreference -ExclusionPath $InstallDir, $StageRoot -ErrorAction Stop; Write-OK "Exclusion added" } catch { Write-Skip "Could not add it (another antivirus may be in charge) - continuing" } } else { Write-Host " Windows will ask for permission - click Yes." -ForegroundColor Yellow try { $enc = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($exclCmd)) $p = Start-Process -FilePath "powershell.exe" -Verb RunAs -WindowStyle Hidden -Wait -PassThru ` -ArgumentList "-NoProfile -ExecutionPolicy Bypass -EncodedCommand $enc" if ($p.ExitCode -eq 0) { Write-OK "Exclusion added" } else { Write-Skip "Could not add it (another antivirus may be in charge) - continuing" } } catch { Write-Skip "Permission not given - continuing without the exclusion" } } # -- 2. Download --------------------------------------------- Write-Step "Downloading (the app can stay open meanwhile)..." New-Item -ItemType Directory -Path $StageExtract -Force | Out-Null try { Download-File -Url $DownloadURL -OutFile $ZipFile $len = (Get-Item $ZipFile -ErrorAction SilentlyContinue).Length if (-not $len -or $len -lt 5MB) { throw "the file looks incomplete ($len bytes)" } Write-OK ("Downloaded " + [math]::Round($len / 1MB, 1) + " MB") } catch { Write-Err "Download failed: $($_.Exception.Message)" Safe-RemoveItem $StageRoot return } # -- 3. Extract ---------------------------------------------- Write-Step "Unpacking..." try { Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop [System.IO.Compression.ZipFile]::ExtractToDirectory($ZipFile, $StageExtract) } catch { try { Expand-Archive -Path $ZipFile -DestinationPath $StageExtract -Force } catch { Write-Err "Unpacking failed: $($_.Exception.Message)" Write-Host " Your antivirus may have blocked a file. Allow it (or add an exclusion for" -ForegroundColor Yellow Write-Host " $InstallDir) and run the command again." -ForegroundColor Yellow Safe-RemoveItem $StageRoot return } } $found = Get-ChildItem -Path $StageExtract -Recurse -Filter $ExeName -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $found) { Write-Err "$ExeName was not in the download (an antivirus may have removed it)." Safe-RemoveItem $StageRoot return } $StagedRoot = Split-Path $found.FullName -Parent Write-OK "Unpacked" # -- 4. Install (close the running app first) ---------------- Write-Step "Installing to $InstallDir ..." Stop-App try { if (Test-Path $InstallDir) { Remove-Item -LiteralPath $InstallDir -Recurse -Force -ErrorAction Stop } New-Item -ItemType Directory -Path (Split-Path $InstallDir -Parent) -Force | Out-Null try { Move-Item -LiteralPath $StagedRoot -Destination $InstallDir -Force -ErrorAction Stop } catch { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null Copy-Item -Path (Join-Path $StagedRoot "*") -Destination $InstallDir -Recurse -Force -ErrorAction Stop } } catch { Write-Err "Install failed: $($_.Exception.Message)" Write-Host " Close Vidancee Desktop and run the command again." -ForegroundColor Yellow Safe-RemoveItem $StageRoot return } Safe-RemoveItem $StageRoot $ExePath = Join-Path $InstallDir $ExeName if (-not (Test-Path $ExePath)) { Write-Err "$ExeName is missing after install."; return } # Files downloaded from the internet carry a "blocked" mark; clear it so Windows # doesn't ask again for every file. Get-ChildItem -LiteralPath $InstallDir -Recurse -File | Unblock-File -ErrorAction SilentlyContinue Write-OK "Installed" # -- 5. Shortcuts -------------------------------------------- Write-Step "Creating shortcuts..." try { $sh = New-Object -ComObject WScript.Shell foreach ($lnk in @($DesktopLink, $StartMenuLink)) { $dir = Split-Path $lnk -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } $sc = $sh.CreateShortcut($lnk) $sc.TargetPath = $ExePath $sc.WorkingDirectory = $InstallDir $sc.IconLocation = $ExePath $sc.Description = "Vidancee Desktop - AI video & image studio" $sc.Save() } Write-OK "Desktop + Start Menu shortcuts" } catch { Write-Skip "Shortcuts could not be created: $($_.Exception.Message)" } # -- Done ---------------------------------------------------- Write-Host "" Write-Host " ============================================" -ForegroundColor Green if ($IsUpdate) { Write-Host " Vidancee Desktop updated! " -ForegroundColor White } else { Write-Host " Vidancee Desktop installed! " -ForegroundColor White } Write-Host " ============================================" -ForegroundColor Green Write-Host "" Write-Host " Next: paste your API key from https://vidancee.com/account.php" -ForegroundColor Gray Write-Host " (Claude connector & API -> make a key; it starts with vdk_)" -ForegroundColor Gray Write-Host "" Write-Host " Starting Vidancee Desktop..." -ForegroundColor Yellow Start-Process -FilePath $ExePath -WorkingDirectory $InstallDir