kitty-direct sends c= but no rows through ConPTY (the terminal guesses the height and stretches); the plain kitty type needs tty pixel queries that ConPTY drops. The iterm type with preserveAspectRatio=1 letterboxes any aspect ratio.
201 lines
8.6 KiB
PowerShell
201 lines
8.6 KiB
PowerShell
# J621Greeting.ps1 - show a random J621 library image as the shell greeting.
|
|
#
|
|
# PowerShell port of extras/fish_greeting/fish_greeting.fish (the J621 REST
|
|
# rewrite): asks GET /api/random/?fastfetch=1&rating=X for a random
|
|
# png/jpg/gif, downloads the signed url and runs fastfetch with the image as
|
|
# its logo. Based on the original script from
|
|
# J621-Django/extras/fish_greeting system/fish_greeting.fish.
|
|
#
|
|
# Requires: fastfetch and curl.exe (ships with Windows 10 1803+).
|
|
#
|
|
# Rating modes (same file name as the fish version):
|
|
# %APPDATA%\j621Greeting\greeting_mode 0 = NSFW (explicit)
|
|
# 1 = SFW (safe)
|
|
# 2 = Questionable (default)
|
|
#
|
|
# Configuration: %APPDATA%\j621Greeting\config.ps1 (sets environment
|
|
# variables; see config.example.ps1):
|
|
# J621_BASE API origin (default https://j621.rainbow-herring.ts.net)
|
|
# J621_TOKEN API / scoped greeting token, sent as "Authorization: Token ..."
|
|
# J621_WEB web origin used in the printed link (defaults to J621_BASE)
|
|
# J621_FASTFETCH_CONFIG fastfetch config name (default "jake")
|
|
# J621_MODE_FILE mode file override
|
|
# J621_LOGO_TYPE force a fastfetch logo type (sixel, kitty-direct, ...)
|
|
# J621_ALWAYS_FETCH=1 fetch even in terminals without image support
|
|
# J621_LOGO_WIDTH logo width in columns (default 35)
|
|
# J621_LOGO_HEIGHT iterm box height in rows (default 25)
|
|
|
|
#Requires -Version 5.1
|
|
|
|
function Get-J621FileKind {
|
|
param([string]$Path)
|
|
try {
|
|
$stream = [IO.File]::OpenRead($Path)
|
|
try {
|
|
$buffer = New-Object byte[] 8
|
|
$read = $stream.Read($buffer, 0, 8)
|
|
if ($read -ge 4 -and $buffer[0] -eq 0x89 -and $buffer[1] -eq 0x50 -and $buffer[2] -eq 0x4E -and $buffer[3] -eq 0x47) { return 'png' }
|
|
if ($read -ge 2 -and $buffer[0] -eq 0xFF -and $buffer[1] -eq 0xD8) { return 'jpeg' }
|
|
if ($read -ge 3 -and $buffer[0] -eq 0x47 -and $buffer[1] -eq 0x49 -and $buffer[2] -eq 0x46) { return 'gif' }
|
|
return 'unknown'
|
|
} finally { $stream.Dispose() }
|
|
} catch { return 'unknown' }
|
|
}
|
|
|
|
function Format-J621Accent {
|
|
param([string]$Text)
|
|
return "$([char]27)[4;2;38;2;138;0;222;49m$Text$([char]27)[0m"
|
|
}
|
|
|
|
function Format-J621Error {
|
|
param([string]$Text)
|
|
return "$([char]27)[31m$Text$([char]27)[0m"
|
|
}
|
|
|
|
function Get-J621FastfetchArgs {
|
|
$configName = if ($env:J621_FASTFETCH_CONFIG) { $env:J621_FASTFETCH_CONFIG } else { 'jake' }
|
|
$candidates = @(
|
|
(Join-Path $env:APPDATA "fastfetch\$configName.jsonc"),
|
|
(Join-Path $env:APPDATA "fastfetch\$configName\config.jsonc")
|
|
)
|
|
foreach ($candidate in $candidates) {
|
|
if (Test-Path -LiteralPath $candidate) { return @('--config', $configName) }
|
|
}
|
|
return @()
|
|
}
|
|
|
|
function Show-J621Greeting {
|
|
param([int]$Depth = 0)
|
|
|
|
# --- Configuration ---
|
|
$configFile = Join-Path $env:APPDATA 'j621Greeting\config.ps1'
|
|
if (Test-Path -LiteralPath $configFile) { . $configFile }
|
|
|
|
$base = if ($env:J621_BASE) { $env:J621_BASE.TrimEnd('/') } else { 'https://j621.rainbow-herring.ts.net' }
|
|
$webBase = if ($env:J621_WEB) { $env:J621_WEB.TrimEnd('/') } else { $base }
|
|
$token = $env:J621_TOKEN
|
|
$logoWidth = if ($env:J621_LOGO_WIDTH) { $env:J621_LOGO_WIDTH } else { '35' }
|
|
$modeFile = if ($env:J621_MODE_FILE) { $env:J621_MODE_FILE } else { Join-Path $env:APPDATA 'j621Greeting\greeting_mode' }
|
|
|
|
# --- Setup logging ---
|
|
$logDir = Join-Path $env:APPDATA 'j621Logos'
|
|
$logFile = Join-Path $logDir 'logs.log'
|
|
if (-not (Test-Path -LiteralPath $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
|
|
$stamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
|
|
|
|
# --- Mode selection ---
|
|
$mode = '2'
|
|
if (Test-Path -LiteralPath $modeFile) {
|
|
$raw = Get-Content -LiteralPath $modeFile -Raw -ErrorAction SilentlyContinue
|
|
if ($null -ne $raw) { $mode = $raw.Trim() }
|
|
}
|
|
switch ($mode) {
|
|
'0' { $rating = 'e'; $modeName = 'NSFW' }
|
|
'1' { $rating = 's'; $modeName = 'SFW' }
|
|
'2' { $rating = 'q'; $modeName = 'Questionable' }
|
|
default { Write-Output 'Unknown mode, using default NSFW'; $rating = 'e'; $modeName = 'NSFW' }
|
|
}
|
|
|
|
# --- Pick the image protocol for this terminal ---
|
|
$logoType = $env:J621_LOGO_TYPE
|
|
if (-not $logoType) {
|
|
if ($env:WT_SESSION) { $logoType = 'sixel' }
|
|
elseif ($env:WEZTERM_PANE) { $logoType = 'iterm' }
|
|
}
|
|
$fetchImage = [bool]$logoType -or ($env:J621_ALWAYS_FETCH -eq '1')
|
|
$ffArgs = @(Get-J621FastfetchArgs)
|
|
|
|
if (-not $fetchImage) {
|
|
# Terminal without image support (e.g. VS Code): plain fastfetch.
|
|
& fastfetch @ffArgs
|
|
return
|
|
}
|
|
|
|
# --- Ask the API for a random image (JSON with a signed URL) ---
|
|
$apiUrl = "$base/api/random/?fastfetch=1&rating=$rating"
|
|
$curlArgs = @('-s', '-L', '--max-time', '20')
|
|
if ($token) { $curlArgs += @('-H', "Authorization: Token $token") }
|
|
$json = & curl.exe @curlArgs $apiUrl 2>$null | Out-String
|
|
|
|
$data = $null
|
|
try { $data = $json | ConvertFrom-Json } catch { $data = $null }
|
|
|
|
$imageUrl = $null; $jId = $null; $md5 = $null; $filename = $null; $detail = $null; $ext = 'img'
|
|
if ($data) {
|
|
if ($data.url) { $imageUrl = [string]$data.url }
|
|
if ($data.j_id) { $jId = [string]$data.j_id }
|
|
if ($data.md5) { $md5 = [string]$data.md5 }
|
|
if ($data.filename) { $filename = [string]$data.filename }
|
|
if ($data.detail) { $detail = [string]$data.detail }
|
|
if ($data.extension) { $ext = [string]$data.extension }
|
|
}
|
|
if ($ext -notmatch '^[A-Za-z0-9]+$') { $ext = 'img' }
|
|
|
|
if (-not $imageUrl) {
|
|
& fastfetch @ffArgs
|
|
if ($detail) { Write-Host (Format-J621Accent "No logo ($detail)") } else { Write-Host (Format-J621Accent 'No logo (API error)') }
|
|
Add-Content -LiteralPath $logFile -Value "$stamp No logo for rating=${rating}: $detail"
|
|
return
|
|
}
|
|
|
|
# --- Download the signed image ---
|
|
$tempFile = Join-Path ([IO.Path]::GetTempPath()) ('j621_logo_' + [guid]::NewGuid().ToString('N') + '.' + $ext)
|
|
$downloadSuccess = $false
|
|
& curl.exe -s -L --max-time 60 -o $tempFile $imageUrl 2>$null
|
|
if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $tempFile) -and (Get-Item -LiteralPath $tempFile).Length -gt 0) { $downloadSuccess = $true }
|
|
|
|
if (-not $downloadSuccess) {
|
|
& fastfetch @ffArgs
|
|
Write-Host (Format-J621Accent 'No logo (download failed)')
|
|
Add-Content -LiteralPath $logFile -Value "$stamp Download failed for $jId"
|
|
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
|
return
|
|
}
|
|
|
|
# --- Accept only PNG, JPEG, GIF; anything else causes one more roll ---
|
|
$kind = Get-J621FileKind -Path $tempFile
|
|
if ($kind -eq 'unknown') {
|
|
if ($Depth -lt 3) {
|
|
Show-J621Greeting -Depth ($Depth + 1)
|
|
} else {
|
|
Write-Host (Format-J621Error 'Max recursion depth reached, skipping unsupported file type: unknown')
|
|
}
|
|
if ($Depth -eq 0) {
|
|
Write-Host (Format-J621Error ("Forked (Got unsupported type: unknown - $filename)"))
|
|
if ($md5) { Write-Output "Link: $webBase/view/$md5" }
|
|
}
|
|
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
|
return
|
|
}
|
|
|
|
# --- Display result ---
|
|
if ($logoType) {
|
|
$logoArgs = @('--logo-type', $logoType, '--logo', $tempFile, '--logo-width', $logoWidth)
|
|
if ($logoType -eq 'iterm') {
|
|
# Box + preserveAspectRatio: never stretched, whatever the aspect ratio
|
|
# (kitty-direct cannot do this through ConPTY: its payload has no rows).
|
|
$logoHeight = if ($env:J621_LOGO_HEIGHT) { $env:J621_LOGO_HEIGHT } else { '25' }
|
|
$logoArgs += @('--logo-height', $logoHeight, '--logo-preserve-aspect-ratio', 'true')
|
|
} elseif ($env:J621_LOGO_HEIGHT) {
|
|
$logoArgs += @('--logo-height', $env:J621_LOGO_HEIGHT)
|
|
}
|
|
& fastfetch @ffArgs @logoArgs
|
|
} else {
|
|
& fastfetch @ffArgs
|
|
}
|
|
Add-Content -LiteralPath $logFile -Value "$stamp Downloaded: $jId $filename [$modeName]"
|
|
if ($filename) {
|
|
Write-Host (Format-J621Accent "Logo from API: $filename ($modeName, $jId)")
|
|
Write-Output "Link: $webBase/detail/$jId"
|
|
} else {
|
|
Write-Host (Format-J621Accent "Logo from API ($modeName)")
|
|
}
|
|
|
|
# --- Keep only the last 1000 lines of the log ---
|
|
$lines = @(Get-Content -LiteralPath $logFile -Tail 1000)
|
|
Set-Content -LiteralPath $logFile -Value $lines
|
|
|
|
# --- Cleanup ---
|
|
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
|
}
|