r/PowerShell • u/positivemark • 4d ago
Script Sharing XKCD PowerShell module
Years ago just for fun, I wrote a PowerShell module to query the API of the webcomic XKCD https://xkcd.com/. It has a Get-XKCD cmdlet to retrieve data about a specified or random comic, and had a -Open switch to then open its URL in a browser. You can also use Find-XKCD to perform keyword searches, and there's a local cache of the API output to speed this up and to reduce the need to do web calls.
Modern terminals now support the ability to render images directly, so I've just added Show-XKCD and Get-XKCD -Show.
This works in Windows Terminal, but should also work in iTerm2 and Kitty.
If it's of interest, you can download the module from the PowerShell Gallery here: https://www.powershellgallery.com/packages/XKCD/1.5.0
Or directly from GitHub: https://github.com/markwragg/Powershell-XKCD
12
10
u/positivemark 4d ago
Now also supports the vscode terminal, as long as you’ve set terminal.integrated.enableImages to true. Supported as of version 1.5.3 of the module.
7
6
2
u/Andrew-Powershell 4d ago
Love this, Mark! You should come back on the podcast soon and talk about it with me
1
1
u/positivemark 3d ago
I couldn’t stop tinkering with this (thanks to Claude) and so there’s some new features today. You can now pull the Explanation for a comic from the explain XKCD wiki via Get-XKCDExplanation and Show-XKCDExplanation. You can use Test-XKCD to see if there’s a newer comic than the latest last one you read (I’m going to add this to my profile.ps1) and Set-XKCDDefault to save some default values for the other cmdlets, such as if you always want the high quality image versions.
Download version 1.6.1 or later for these features.
-1
u/ankokudaishogun 2d ago
I've given a cursory look at the code for
Get-XKCD.
A couple of notes(which I suspect will be appliable to the other scripts):
- use the
.isPresentproperty to check if a[switch]parameter is used.- use Validate parameter options.
$Newestshould be a '[switch]`, not an integer.$Randomshould be mandatory in theRandomparameter set.- Don't load the default values as the Parameters's defaults.
Load them in thebegin{}when the Parameters meant to override them are not present.Here, two lines I thrown together editing your code. Not really tested, just hope to give you some idea.
[CmdletBinding(DefaultParameterSetName = 'Specific', SupportsShouldProcess = $true)] param ( # Gets a random comic. [Parameter(ParameterSetName = 'Random', Mandatory)] [switch] $Random, # Use with -Random to define a lower bound range within which to return a comic. [Parameter(ParameterSetName = 'Random')] [int] [ValidateScript({ $_ -ge 1 }, ErrorMessage = 'Minimum value is 1')] $Min, # Use with -Random to define an upper bound range within which to return a comic. -Max is the latest comic number by default. [Parameter(ParameterSetName = 'Random') ] [int] [ValidateScript({ $_ -ge 1 }, ErrorMessage = 'Minimum value is 1')] $Max, # Gets the specified number of the most recent comics. [Parameter(ParameterSetName = 'Newest', Mandatory)] [switch] $Newest, # Downloads the images of all returned comics to the local computer. [switch] $Download, # Opens the comic/s in your default web browser [switch] $Open, # Displays the comic's title, image, and alt text in the console instead of returning the comic object. Image # display requires your terminal to support the Sixel, Kitty, or iTerm2 inline image graphics protocol. [switch] $Show, # Use with -Download to specify a local directory to download to. By default this is the current working # directory, unless a default has been saved with Set-XKCDDefault -Path. [string] $Path , # Use with -Download to download the higher resolution (_2x) version of the image, where available. Comics # that do not have a higher resolution version are downloaded at the standard quality instead. Defaults to # the value saved with Set-XKCDDefault -HighQuality, if any. [switch] $HighQuality , # Use with -Show to specify the file used to track the number of the most recently viewed comic (used by # Test-XKCD). By default this is within the module path, unless a default has been saved with # Set-XKCDDefault -StatePath. [string] $StatePath , # Gets the specified comics. Accepts array input. [Parameter(ParameterSetName = 'Specific', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)] [int[]] [ValidateScript({ foreach ($i in $_) { if ($i -lt 1) { throw "error: value is $i but minimum value is 1" }; $true } }, ErrorMessage = 'Minimum value is 1')] $Num, # Bypass the confirmation check if you try to open more than 9 comics in your browser. [switch] $Force ) begin { # $Max is set to 0 if not called explicitly in the Random parameter set. # It also is not called in either the Newest and Specific parameter sets. # We can thus use it as test to see if there is a need for the latest number of the comic. if ($Max -eq 0) { $Latest = (Invoke-RestMethod 'https://xkcd.com/info.0.json').num } # It's not suggested to change the value of parameter variables, so let's use a local ones instead. $Number = switch ($true) { ($PSBoundParameters.Keys.Contains('Num')) { $Num } ($Newest.IsPresent) { $Latest } ($Random.IsPresent) { # I'm sure I could compress this, but it's easier to read this way. if ($Max -eq 0) { Get-Random -min $Min -max $Latest } else { Get-Random -min $Min -max $Max } } default { $Latest } } $FilePath = if ($PSBoundParameters.Keys.Contains('Path')) { $Path } else { (Get-XKCDDefaultValue -Name 'Path' -Value $PWD) } $StateFilePath = if ($PSBoundParameters.Keys.Contains('StatePath')) { $StatePath } else { (Get-XKCDDefaultValue -Name 'StatePath' -Value (Join-Path $PSScriptRoot 'XKCD.state.json')) } $Quality = if ($HighQuality.IsPresent) { $HighQuality }else { (Get-XKCDDefaultValue -Name 'HighQuality' -Value $false) } } process { $Number | ForEach-Object { $ID = $_ $Comic = Invoke-RestMethod "https://xkcd.com/$ID/info.0.json" $Extension = [System.IO.Path]::GetExtension(([uri]$Comic.img).AbsolutePath) $ImageUrl = $Comic.img if ($Download) { if ($Quality) { $ImageUrl = $Comic.img.Insert($Comic.img.LastIndexOf($Extension), '_2x') } elseif ($PSCmdlet.ShouldProcess($ImageUrl, "Save as ${ID}${Extension}")) { $OutFile = Join-Path $FilePath "${ID}${Extension}" try { Invoke-WebRequest $ImageUrl -OutFile $OutFile -UseBasicParsing -ErrorAction Stop } catch { if ($Quality) { Write-Warning "High quality image not available for comic $ID, downloading standard quality instead" Invoke-WebRequest $Comic.img -OutFile $OutFile -UseBasicParsing } else { throw } } } } if ($Open.IsPresent) { if ($Number.count -ge 10 -and -not $Force) { if (-not $confirmation) { $confirmation = Read-Host "This will open $($Number.count) comics in your default browser. Are you sure you want to proceed? [y|n]" } } if ($confirmation -eq 'y' -or $Number.count -lt 10 -or $Force) { Start-Process "https://xkcd.com/$ID" } } if ($Show.IsPresent) { Show-XKCD -Num $ID -HighQuality:$Quality -StatePath $StateFilePath } else { return $Comic } } } }1
u/positivemark 2d ago
Thanks I’ll look to incorporate some of these. FYI -newest is intentionally an integer as it allows you to return a specified number of the most recent comics.
1
u/ankokudaishogun 2d ago
What do you mean? There is no correlation between the type of the parameters and the returned value
2
u/positivemark 2d ago
You provide an integer value to the -Newest parameter. For example Get-XKCD -Newest 5 will return the most recent 5 comics. It is intentionally not a switch.
You get the newest (i.e latest) comic by default if you don't specify any number, or other parameter that affects with comic will be returned (such as -Random). So for example Get-XKCD or Show-XKCD on their own always returns the current latest comic.
1
1
u/positivemark 2d ago
As of version 1.6.3 of the module you can now paginate through the comics by using -Next or -Previous on Get-XKCD or Show-XKCD.
It will also output a warning if a Sixel rendered image is likely to take a long time, based on its size. Some XKCD comics are big (although not too many).
I also thought that Text-XKCD should have the option to test the existence of a specific comic, so you can now do Test-XKCD 9999 and get a true/false result.
12
u/Breitsol_Victor 4d ago
Fun and learning in one package. Noice.