# Teleport Export/v1.105 $TScriptName = "Teleport Export/v1.105" $TSUserAgent = "Teleport-Export-PS/1.105" $Iso8601Format = "yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z" Add-Type -AssemblyName System.Net.Http Add-Type -AssemblyName System.Runtime function Parse-Date ([string]$date) { $result = Get-Date $FileNameTimeFormat = "yyyyMMdd\THHmmss" $Convertible = [DateTime]::TryParseExact( $date, $Iso8601Format, [system.Globalization.DateTimeFormatInfo]::InvariantInfo, #info independent on the current system settings [system.Globalization.DateTimeStyles]::None, <#the format must be matched exactly, no whitespace or anything more info here: http://msdn.microsoft.com/cs-cz/library/ms131044.aspx #> [ref]$result #save the result here, if the operation fail the result is invalid ) if ($Convertible) { return $result } $Convertible = [DateTime]::TryParseExact( $date, $FileNameTimeFormat, [system.Globalization.DateTimeFormatInfo]::InvariantInfo, #info independent on the current system settings [system.Globalization.DateTimeStyles]::None, <#the format must be matched exactly, no whitespace or anything more info here: http://msdn.microsoft.com/cs-cz/library/ms131044.aspx #> [ref]$result #save the result here, if the operation fail the result is invalid ) if ($Convertible) { return $result } #try any format $Convertible = [DateTime]::TryParse( $date, [ref]$result ) if ($Convertible) { return $result } } Function Teleport-Export { <# .SYNOPSIS . .DESCRIPTION Exports still image frames stored in Teleport. You can specify the start/end time and the interval between exported frames. Also frame size can be specified. .EXAMPLE Teleport-Export -feedId feedidgoeshere -startTime 20140601T000000 -endTime 20140901T000000 -interval 20000 -sizeCodes ('x480', 'x768', 'x1080', 'x2160') -key keygoeshere -exportFolder 'C:\TeleportExport\' #> [CmdletBinding()] Param ( [parameter(Mandatory = $true, HelpMessage = "Id for feed to export. Get this from the feed url, after /feed/")] [ValidateNotNullOrEmpty()] # Id for feed to export. Get this from the feed url, after /feed/ [string]$feedId, [parameter(Mandatory = $true, HelpMessage = "Time in UTC Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss).")] #Time in UTC Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss). [string]$startTime, [parameter(Mandatory = $true, HelpMessage = "Time in UTC Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss).")] #Time in UTC Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss). [string]$endTime, [parameter(Mandatory = $true, HelpMessage = "Time between exported frames in seconds. Use 0 to export all frames.")] #Time between exported frames in seconds. Use 0 to export all frames. [int]$interval = 0, [parameter(Mandatory = $true, HelpMessage = "ApiKey or an AccessKey.")] #ApiKey or an AccessKey. [string]$key, [parameter(HelpMessage = "Image sizes to export, value is a list of x4320, x2160, x1080, x768, x480. Defaults to largest available size. x4320 downloads the largest size available.")] [ValidateSet("x4320", "x2160", "x1080", "x768", "x480")] #Feed sizes to export, values are one of x4320, x2160, x1080, x768, x480, defaults to largest size. i.e. ('x480', 'x1080') to specify multiple. [string[]]$sizeCodes = @('x4320'), [parameter(Mandatory = $false, HelpMessage = "Folder to export to, defaults to 'C:\TeleportExport'")] #Folder to export to, defaults to 'C:\TeleportExport' [string]$exportFolder, [ValidateSet("single-folder", "year-month-day-folder", "year-month-folder", "year-folder")] [string]$exportFolderFormat, [parameter(Mandatory = $false, HelpMessage = "Does not ask for confirmations.")] #Does not ask for confirmations [boolean]$automate ) Process { Write-Host Write-Host $TScriptName Write-Host "-----------------------------------------" if (!$exportFolder) { $exportFolder = 'C:\TeleportExport' } if (!$sizeCodes) { $sizeCodes = 'x4320' } $exportFolder = [System.io.path]::Combine($exportFolder , $feedId) if (!$exportFolderFormat) { $exportFolderFormat = "single-folder" } switch ($exportFolderFormat) { "single-folder" { #all files single folder $FilePathAndNameTimeFormat = "yyyyMMdd\THHmmss" } "year-folder" { #files in year folders $FilePathAndNameTimeFormat = "yyyy\\yyyyMMdd\THHmmss" } "year-month-folder" { #files in year/month folders $FilePathAndNameTimeFormat = "yyyy\\MM\\yyyyMMdd\THHmmss" } "year-month-day-folder" { #files in year/month/day folders $FilePathAndNameTimeFormat = "yyyy\\MM\\dd\\yyyyMMdd\THHmmss" } } $apiBaseUrl = "http://www.teleport.io/api/v2" #$nowTime = Get-Date #$startTime = $nowTime.Date.AddDays(-1).ToString($Iso8601Format, [CultureInfo]::InvariantCulture) #$endTime = $nowTime.ToString($Iso8601Format, [CultureInfo]::InvariantCulture) $startTimeAsDateTime = Parse-Date($startTime) if (!$startTimeAsDateTime) { Write-Host "Unable to parse start time, use Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss). All times in UTC." return } $endTimeAsDateTime = Parse-Date($endTime) if (!$endTimeAsDateTime) { Write-Host "Unable to parse end time, use Iso8601 format (yyyy-MM-ddTHH:mm:ss.FFFFFFFZ) or (yyyyMMddTHHmmss). All times in UTC." return } $startTime = $startTimeAsDateTime.ToString($Iso8601Format, [CultureInfo]::InvariantCulture) $endTime = $endTimeAsDateTime.ToString($Iso8601Format, [CultureInfo]::InvariantCulture) if (!$interval) { $interval = 0 } Write-Host "FeedId : $feedId" Write-Host "StartTime : $startTime" Write-Host "EndTime : $endTime" Write-Host "Inteval : $interval" Write-Host "Size codes : $sizeCodes" Write-Host "Export folder : $exportFolder" Write-Host "-----------------------------------------" Write-Host "Querying frames.." $requestUrl = $apiBaseUrl + "/frame-query?feedid=$feedId&starttime=$startTime&endtime=$endTime&interval=$interval&apikey=$key" #$requestUrl try { $frameTimes = Invoke-WebRequest -Uri $requestUrl -UseBasicParsing -UserAgent $TSUserAgent $frameTimes = ConvertFrom-Json $frameTimes #won't work for > 2MB requests, no way to set the MaxJsonLength for the command #$frameTimes = $frameTimes | ConvertFrom-Json } catch [System.Net.WebException] { $HttpWebResponse = [System.Net.HttpWebResponse]$_.Exception.Response $rs = $HttpWebResponse.GetResponseStream() $rs.Position = 0 $sr = new-object System.IO.StreamReader $rs $content = $sr.ReadToEnd() Write-Host $content Write-Host "Error, aborting export." return } Write-Host "Found $($frameTimes.Frames.Length) frames" if ($frameTimes.Frames.Length -eq 0) { Write-Host "Nothing export." return } if (!$automate) { [ValidateSet("Y", "N")]$Answer = Read-Host "Proceed with export? y/n" if ($Answer -ne "y") { Write-Host "Aborting export." return } } Write-Host "-----------------------------------------" Write-Host "Beginning export.." If (!(Test-Path $exportFolder)) { New-Item -Path $exportFolder -ItemType "directory" | Out-Null } If (!(Test-Path $exportFolder)) { Write-Host "Unable to create $exportFolder" return } #[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $clnt = New-Object System.Net.Http.HttpClient $clnt.DefaultRequestHeaders.Add("user-agent", $TSUserAgent) $timer = [System.Diagnostics.Stopwatch]::StartNew() $exportFrameCount = 0 $exportFileCount = 0 $abort = $false Foreach ($frameTime in $frameTimes.Frames) { Write-Host $frameTime -nonewline Foreach ($sizeCode in $sizeCodes) { $fileTimestamp = [DateTime]::Parse($frameTime) $targetFilePath = [System.IO.Path]::Combine($exportFolder, "$($fileTimestamp.ToString($FilePathAndNameTimeFormat, [CultureInfo]::InvariantCulture))-$sizeCode.jpg") # make sure the folder exists [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($targetFilePath)) #Write-Host "Using : $targetFilePath" $imageUrl = $apiBaseUrl + "/frame-get?feedid=$feedId&frametime=$frameTime&sizecode=$sizeCode&apikey=$key" Write-Host " $sizeCode" -nonewline #Write-Host " $imageUrl" -nonewline #$imageUrl #$targetFilePath #continue #Invoke-WebRequest -Uri $requestUrl -UseBasicParsing # if previous export failed for whatever reason, don't try to -re-download already existing frames # remove this test if you'd like to overwrite existing files, note this may cause excessive bandwidth usage If (Test-Path $targetFilePath) { Write-Host " OK*" -ForegroundColor Yellow -nonewline $exportFileCount++ continue; } $retryAttempt = 0; while ($true) { try { $task = $clnt.GetAsync($imageUrl) $response = $task.Result $statusCode = $response.StatusCode.value__ #Write-Host " $statusCode" -NoNewline if ($statusCode -ne 200) { $errorInfo = $statusCode; If ($statusCode -eq 404) { #skip, due to missing feed size or mean time deletions Write-Host " SKIP" -ForegroundColor Yellow -nonewline break; } $retryAttempt++; iF ($retryAttempt -gt 5) { $abort = $true break } Write-Host " Error $errorInfo" -ForegroundColor Red -NoNewline #Write-Host "$frameTime $sizeCode" -nonewline Write-Host " Retry.." -ForegroundColor Yellow -nonewline $waitTime = [Math]::Floor([Math]::Pow( $retryAttempt, 2 )) Start-Sleep -s $waitTime continue; } $outputFileStream = [System.IO.FileStream]::new($targetFilePath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write) $response.Content.CopyToAsync($outputFileStream).Wait() $outputFileStream.Close() Write-Host " OK" -ForegroundColor Yellow -nonewline $exportFileCount++ break; } catch [System.Exception] { $errorInfo = $_.Exception.ToString() $retryAttempt++; iF ($retryAttempt -gt 10) { $abort = $true break } Write-Host " Error $errorInfo" -ForegroundColor Red -NoNewline #Write-Host "$frameTime $sizeCode" -nonewline Write-Host " Retry.." -ForegroundColor Yellow -nonewline $waitTime = [Math]::Floor([Math]::Pow( $retryAttempt, 2 )) Start-Sleep -s $waitTime } finally { if ($null -ne $response) { $response.Dispose() } } } if ($abort) { Write-Host " ABORT" -ForegroundColor Red -nonewline } } Write-Host $exportFrameCount++ } $timer.Stop(); Write-Host "-----------------------------------------" Write-Host "Exported $exportFrameCount frames in $exportFileCount files, " -NoNewline Write-Host $([string]::Format("at {0:f2} fps.", $timer.Elapsed.TotalSeconds)) } } If ($myinvocation.InvocationName -eq ".") { Write-Host Write-Host $TScriptName Write-Host "-----------------------------------------" #Write-Host "Dot source the script using '. .\Teleport-Export.ps1', then Type 'Teleport-Export' or 'Teleport-Export -?' to use this script." Write-Host "Functions loaded. Type 'Teleport-Export' to export or 'get-help Teleport-Export -detailed' for help." Write-Host } else { If ($args) { Teleport-Export @args #using splatting } else { Teleport-Export } }