Last active
August 26, 2025 12:36
-
-
Save SQLDBAWithABeard/daf0f7b5493c6a5f588029ed2a9a7b69 to your computer and use it in GitHub Desktop.
trail cam timelaps to video script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Start-TimelapseProcessing { | |
| <# | |
| .SYNOPSIS | |
| Copies image files from SD card, renames them, creates a timelapse video using ffmpeg, and compresses it with HandBrake. | |
| - Source: D:\DCIM\100MEDIA | |
| - Destination: $env:USERPROFILE\OneDrive\Desktop\100media\<date> | |
| - Renames files to lowercase | |
| - Creates timelapse video (mp4) | |
| - Compresses video with HandBrake | |
| #> | |
| # Variables | |
| $folder = Join-Path $env:USERPROFILE 'OneDrive\Desktop\100media' | |
| $date = Get-Date -Format yyyyMMdd | |
| $destinationFolder = "$folder\$date" | |
| $sdcardPath = 'D:\DCIM\100MEDIA' | |
| $imagePattern = "$destinationFolder\IMAG*.jpg" | |
| $ffmpegFolder = "$folder\$date\imag%04d.jpg" | |
| $ffmpegOutput = "$folder\$date-output.mp4" | |
| $handbrakeOutput = "$folder/$date-handbrakeoutput.mp4" | |
| $handbrakeCLI = './HandBrakeCLI' | |
| $handbrakeDrive = 's:' | |
| $zipFile = "$destinationFolder\$date-images.zip" | |
| $networkpath = '\\Big_Beard_NAS\video\Timelapse\BillyCam\' | |
| $zippedFolder = "$networkpath\zippedFiles" | |
| $startTime = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | |
| Write-PSFMessage -Level Significant -Message "Script started at $startTime" | |
| write-PSFMessage -Level Significant -Message "Stop OneDrive sync temporarily to avoid file lock issues." | |
| Get-Process onedrive | Stop-Process -Force | |
| Write-PSFMessage -Level Host -Message "Starting script to copy files from SD card, rename them, create a timelapse video, and compress it with HandBrake." | |
| Write-PSFMessage -Level Significant -Message "Checking if destination folder exists: $destinationFolder" | |
| if (!(Test-Path -Path $destinationFolder)) { | |
| Write-PSFMessage -Level Important -Message "Creating destination folder: $destinationFolder" | |
| New-Item -ItemType Directory -Path $destinationFolder | Out-Null | |
| } | |
| Write-PSFMessage -Level Significant -Message "Getting files from SD card: $sdcardPath" | |
| $sdcardfiles = Get-ChildItem -Path $sdcardPath | Sort-Object CreationTime | |
| if ($sdcardfiles.Count -eq 0) { | |
| Write-PSFMessage -Level Warning -Message "No files found in $sdcardPath" | |
| break | |
| } else { | |
| Write-PSFMessage -Level Important -Message "Copying files from SD card to $destinationFolder" | |
| $i = 1 | |
| $prefix = "imag" | |
| $extension = ".jpg" | |
| foreach ($file in $sdcardfiles) { | |
| $newName = "{0}{1:D4}{2}" -f $prefix, $i, $extension | |
| $destinationPath = Join-Path $destinationFolder $newName | |
| Write-PSFMessage -Level Verbose -Message "Copying $($file.FullName) to $destinationFolder" | |
| Copy-Item -Path $file.FullName -Destination $destinationPath | |
| $i++ | |
| } | |
| } | |
| Write-PSFMessage -Level Important -Message "Creating timelapse video with ffmpeg: $ffmpegOutput" | |
| $ffmpegexpression = 'ffmpeg -framerate 24 -i "{0}" -c:v libx264 -pix_fmt yuv420p "{1}"' -f $ffmpegFolder, $ffmpegOutput | |
| Invoke-Expression $ffmpegexpression | |
| # If the ffmpeg output file doesn't exist, stop the script | |
| if (!(Test-Path -Path $ffmpegOutput)) { | |
| Write-PSFMessage -Level Error -Message "ffmpeg output file does not exist: $ffmpegOutput" | |
| break | |
| } else { | |
| Write-PSFMessage -Level Important -Message "ffmpeg output file created successfully: $ffmpegOutput" | |
| } | |
| $handbrakeOutputMsg = "Compressing video with HandBrakeCLI: $handbrakeOutput" | |
| Write-PSFMessage -Level Important -Message $handbrakeOutputMsg | |
| Set-Location -Path $handbrakeDrive | |
| $handbrakeCmd = "$handbrakeCLI -i $ffmpegOutput -o '$handbrakeOutput' -f mp4 -t 1 --angle 1 -c 1-1 -m -e x264 -q 22 --encoder-preset fast --encoder-profile main --encoder-level 4.0 --decomb=mode=7 --comb-detect=mode=3:spatial-metric=2:spatial-thresh=1:motion-thresh=1:block-thresh=40:block-width=16:block-height=16 -r 30 --cfr --subtitle scan --subtitle-forced --subtitle-burned --audio-copy-mask aac --audio-fallback av_aac" | |
| Write-PSFMessage -Level Significant -Message "Running HandBrakeCLI command: $handbrakeCmd" | |
| Invoke-Expression $handbrakeCmd | |
| # Validate that the HandBrake output file exists and is smaller than the ffmpeg output file but not zero bytes; if it is, delete the ffmpeg output file | |
| if (Test-Path -Path $handbrakeOutput) { | |
| $ffmpegFileInfo = Get-Item -Path $ffmpegOutput | |
| $handbrakeFileInfo = Get-Item -Path $handbrakeOutput | |
| if ($handbrakeFileInfo.Length -lt $ffmpegFileInfo.Length -and $handbrakeFileInfo.Length -gt 0) { | |
| Write-PSFMessage -Level Important -Message "HandBrake output file is valid. Deleting ffmpeg output file: $ffmpegOutput" | |
| Remove-Item -Path $ffmpegOutput | |
| } else { | |
| Write-PSFMessage -Level Warning -Message "HandBrake output file is not smaller than ffmpeg output file or is zero bytes. Keeping both files." | |
| } | |
| } else { | |
| Write-PSFMessage -Level Error -Message "HandBrake output file does not exist: $handbrakeOutput" | |
| break | |
| } | |
| # Creates a copy of the HandBrake output file to the network path | |
| if (Test-Path -Path $handbrakeOutput -ErrorAction SilentlyContinue) { | |
| $destinationPath = Join-Path $networkpath (Split-Path -Path $handbrakeOutput -Leaf) | |
| Write-PSFMessage -Level Important -Message "Copying HandBrake output file to network path: $destinationPath" | |
| Copy-Item -Path $handbrakeOutput -Destination $destinationPath | |
| } else { | |
| Write-PSFMessage -Level Error -Message "HandBrake output file does not exist: $handbrakeOutput" | |
| } | |
| ## zip all the raw image files in the destination folder and move them to the network path | |
| Write-PSFMessage -Level Important -Message "Zipping raw image files in destination folder: $destinationFolder" | |
| if (Test-Path -Path $zipFile) { | |
| Write-PSFMessage -Level Warning -Message "Zip file already exists. Deleting existing zip file: $zipFile" | |
| Remove-Item -Path $zipFile | |
| } | |
| Compress-Archive -Path $imagePattern -DestinationPath $zipFile | |
| # Creates date directory in network path if it doesn't exist and copies the zipped raw image files there | |
| if (!(Test-Path -Path $zippedFolder)) { | |
| Write-PSFMessage -Level Important -Message "Creating zipped files directory in network path: $zippedFolder" | |
| New-Item -ItemType Directory -Path $zippedFolder | |
| } | |
| Write-PSFMessage -Level Important -Message "Moving zipped raw image files to network path: $zippedFolder" | |
| Move-Item -Path $zipFile -Destination "$zippedFolder" | |
| # delete all raw image files on sd card and processed folder after confirming the zip file exists in the network path | |
| if (Test-Path -Path "$zippedFolder\$date-images.zip") { | |
| Write-PSFMessage -Level Important -Message "Zip file exists in network path. Deleting raw image files from SD card and processed folder." | |
| Write-PSFMessage -Level Important -Message "Deleting raw image files from SD card: $sdcardPath" | |
| Get-ChildItem -Path $sdcardPath | Remove-Item -Force | |
| Write-PSFMessage -Level Important -Message "Deleting raw image files from processed folder: $destinationFolder" | |
| Get-ChildItem -Path $destinationFolder -Filter "imag*.jpg" | Remove-Item -Force | |
| } else { | |
| Write-PSFMessage -Level Warning -Message "Zip file does not exist in network path. Not deleting raw image files." | |
| } | |
| write-PSFMessage -Level Significant -Message "Restarting OneDrive sync." | |
| # Restart OneDrive | |
| Start-Process -FilePath "C:\Program Files\Microsoft OneDrive\OneDrive.exe" -ArgumentList "/background" | |
| $endTime = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | |
| $duration = (New-TimeSpan -Start $startTime -End $endTime).ToString("hh\:mm\:ss") | |
| Write-PSFMessage -Level Important -Message "Script completed at $endTime and took $duration to run." | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Just when I thought I was out, they pull me back in. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment