Skip to content

Instantly share code, notes, and snippets.

View joshooaj's full-sized avatar

Josh Hendricks joshooaj

View GitHub Profile
@joshooaj
joshooaj / Get-LineEnding.ps1
Last active May 29, 2025 21:16
Find files with specific line endings or mixed line endings
function Get-LineEnding {
<#
.SYNOPSIS
Returns the line ending found in a given file.
.DESCRIPTION
Returns a [PSCustomObject] with a `LineEnding` property with the value
'CRLF', 'LF', or 'MIXED' depending on the line endings found in the file.
.EXAMPLE
@joshooaj
joshooaj / Get-DevicePack.ps1
Created May 27, 2025 18:21
Retrieve Milestone Supported Hardware List data
function Get-DevicePack {
<#
.SYNOPSIS
Get the device pack from Milestone's online supported hardware list.
.DESCRIPTION
This command retrieves the device pack data from Milestone's support
hardware list available at https://www.milestonesys.com/support/software/supported-devices/xprotect/.
The data does not include the detailed information available when clicking
@joshooaj
joshooaj / README.md
Last active May 22, 2025 18:13
Traefik reverse-proxy configuration example for XProtect VMS

Running XProtect behind Traefik

Observations

  • The DNS names used by clients to access XProtect must match the DNS names XProtect is configured to use. If the client cannot resolve the DNS names seen in Management Client, things will not work regardless of what DNS name you use to login to XProtect. This is because after a successful login, the client receives a configuration from the server with a list of recording server URLs, registered service URLs, and more.

  • Because of the DNS thing explained above, you'll have to come up with your own strategy for handling DNS resolution on the proxy server(s) and/or clients. Essentially, you need to do split-horizon DNS resolution where clients resolve to the address of the reverse-proxy while the reverse-proxy resolves the same DNS names to the actual Milestone server(s).

  • So far I am still having trouble logging in with Smart Client using a Windows user through Traefik. The IDP component on the Management Server logs an error. But I can login using a basi

@joshooaj
joshooaj / watch-diskfreespace.ps1
Last active March 18, 2025 19:08
Call the GetDiskFreeSpaceExW function from PowerShell using p/invoke
<#
.SYNOPSIS
Gets the free space on a disk and waits 500ms or for a specified delay before checking again.
.DESCRIPTION
This function uses p/invoke to call the GetDiskFreeSpaceEx function from the kernel32.dll library to get the free
space on a disk based on the path provided. It will keep checking the free space at the specified interval, or 500ms
by default, until an error occurs.
When an error occurs, the function will include the output of GetLastError and the string-formatted
@joshooaj
joshooaj / convert.ps1
Last active January 22, 2025 23:08
Convert all video in one or more XProtect database to mkv files
#require -Modules MilestonePSTools, MilestoneSystems.PowerShell.MediaDB
param(
# Specifies the path to a folder containing one or more XProtect media database folders.
[Parameter()]
[string]
$Source,
# Specifies the path to a folder where MKV files will be saved.
[Parameter()]
[string]
@joshooaj
joshooaj / parallel-exports.ps1
Created January 13, 2025 20:35
Export recording sequences in parallel
#requires -Modules Microsoft.PowerShell.ThreadJob
<#
This is an example of how you could parallelize exports. You _probably_ wouldn't do this specific thing in practice
but it was an easy proof of concept on a small system.
The following script will get all enabled cameras, and then get all recording sequences for todays date, and then
using the Start-ThreadJob command, it will run each export in a separate job, saving each recording sequence found
on each enabled camera to it's own MKV file in the C:\temp directory.
#>
@joshooaj
joshooaj / New-RandomPass.ps1
Last active December 10, 2024 23:31 — forked from steviecoaster/New-RandomPass.ps1
Cross platform password generator
function New-RandomPass {
<#
.Synopsis
Generates and returns a suitably secure password
.EXAMPLE
New-RandomPass
Returns a random password as a SecureString object
.EXAMPLE
@joshooaj
joshooaj / ParsePng.ps1
Created December 3, 2024 21:30
Parse PNG files into their individual chunks
function ParsePng {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]
$Path
)
process {
try {
@joshooaj
joshooaj / New-QueryString.ps1
Last active November 25, 2024 22:52 — forked from steviecoaster/New-QueryString.ps1
Creates a querystring to use with an API call
function New-QueryString {
<#
.SYNOPSIS
Turn a hashtable into a URI querystring
.DESCRIPTION
Turn a hashtable into a URI querystring
.PARAMETER QueryParameter
The hashtable to transform
@joshooaj
joshooaj / Send-VmsDriverCommand.ps1
Last active March 30, 2025 23:11
Send custom driver commands to XProtect
function Send-VmsDriverCommand {
<#
.SYNOPSIS
Sends a custom HTTP request or command to a device added to an XProtect VMS.
.PARAMETER Camera
Specifies the camera to which the driver command should be sent.
.PARAMETER Fqid
Specifies the FQID associated with the destination device. Try `Get-VmsVideoOSItem` to retrieve an item, and then