New Video Cleaner
version 2.0.3

About
This is a Windows PowerShell script that removes extra bytes from the end of MP4, MOV, and WMV video files. If such extra bytes are found and removed, they are saved to a text file with the same name as the video file for examination.
Does it work?
I have tested it thoroughly myself and it has worked every time. I use it myself instead of the old cleaner.
Disclaimer
The script is not copyrighted and contains no copyrighted material. It is published here under the MIT license.
Although the software is thoroughly tested and the risk of something wrong is going small, the software is provided as-is without any guarantees.
📼 Making a temporary copy of files before cleaning them is recommended in case something goes wrong.
How to use
- Create a new text file anywhere on your computer. The desktop will do, or in the same folder as your video files.
- Copy the code from the section below, 👇 The Code, using the handy copy button in the top corner.
- Paste that code into the text file.
- Rename the text file
videocleaner.ps1or whatever name you prefer. - Run the script one of two ways…
Prompt mode (recommended) 🔥
Right-click on the script file and choose Run with PowerShell. The script will open.
Basic usage:
- To clean one file, drag it onto the script window and the filename will be entered for you. Then hit enter.
- To clean multiple files, enter all the filenames separated by spaces. You can still use drag-and-drop for this but remember to put a space bewteen the filenames.
- You can add
-yto your input if you want to skip confirmation and clean all the files if any extra bytes are found.
Direct mode
You can also call this script from PowerShell if you want. You would call it like this:
powershell.exe -ExecutionPolicy Bypass -File .\videocleaner.ps1
where videocleaner.ps1 is whatever filename you chose.
The options and syntax for direct mode are the same as for prompt mode. Example for cleaning multiple files and bypassing confirmation:
powershell.exe -ExecutionPolicy Bypass -File .\videocleaner.ps1 file1.mp4 file2.mp4 "filename with spaces.mov" -y
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | # Video Cleaner 2.0.3
# Set the execution policy for the current scope
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# Function to display help information
function Show-Help {
Write-Host @"
Video Cleaner 2.0.3 - MIT License
This script detects and removes trailing junk data from video files.
There are two ways to run this script:
1. Prompt Mode (*Recommended)
Right-click on the script file and choose "Run with PowerShell".
2. Direct Mode
Open PowerShell and run the following command:
powershell.exe -ExecutionPolicy Bypass -File .\<script_name>.ps1
Replace <script_name>.ps1 with the actual name of the script file.
To use the script, provide at least one video file path as an argument.
You can enter multiple file paths separated by spaces. Alternatively,
you can drag and drop the video file(s) onto the PowerShell window.
If trailing junk data is found in a video file, the script will prompt
you for confirmation before removing it. To automatically remove
trailing junk data without confirmation, use the -y flag. The position
of the -y flag does not matter; it can be placed before or after the
file paths.
Examples of Direct mode use:
.\<script_name>.ps1 "C:\path\to\video1.mp4" "C:\path\to\video2.mp4"
.\<script_name>.ps1 -y "C:\path\to\video1.mp4" "C:\path\to\video2.mp4"
Disclaimer:
The author assumes no responsibility for your use of this script,
whether it fails to remove data or renders video files unplayable.
The user is responsible for making backups of videos before using the
script to alter the files. The script itself is free of copyright and
is provided under the MIT license.
"@
}
# Function to detect the file type based on the file header
function Get-FileType($header) {
if ($header -match "^.{4}(ftyp|moov|mdat|free|skip)") {
return "mp4/mov"
}
elseif ($header.SubString(0, 4) -eq [System.Text.Encoding]::ASCII.GetString([byte[]] @(0x30, 0x26, 0xb2, 0x75))) {
return "wmv"
}
else {
return "unknown"
}
}
# Function to get the minimum chunk size based on the file type
function Get-MinChunkSize($fileType) {
switch ($fileType) {
"mp4/mov" { return 8 }
"wmv" { return 24 }
default { throw "Unknown file type" }
}
}
# Function to find the trailing junk bytes
function Find-TrailingJunkBytes($filePath) {
$fileStream = New-Object System.IO.FileStream($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$header = New-Object byte[] 16
$null = $fileStream.Read($header, 0, 16)
$fileType = Get-FileType ([System.Text.Encoding]::ASCII.GetString($header))
if ($fileType -eq "unknown") {
Write-Host " Unsupported file type: $filePath`n" -ForegroundColor $Host.PrivateData.WarningForegroundColor -BackgroundColor $Host.PrivateData.WarningBackgroundColor
$fileStream.Close()
return -1
}
$minChunkSize = Get-MinChunkSize $fileType
$fileSize = $fileStream.Length
Write-Host " File type: $fileType, Size: $fileSize bytes"
$position = 0
$lastValidChunkEnd = 0
while ($position -lt $fileSize) {
$remainingBytes = $fileSize - $position
$fileStream.Position = $position
if ($remainingBytes -lt $minChunkSize) {
break
}
$chunkHeader = New-Object byte[] $minChunkSize
$bytesRead = $fileStream.Read($chunkHeader, 0, $minChunkSize)
if ($bytesRead -lt $minChunkSize) {
break
}
# Decode chunk length based on file type
$chunkLength = switch ($fileType) {
"mp4/mov" { [System.Net.IPAddress]::NetworkToHostOrder([BitConverter]::ToInt32($chunkHeader, 0)) }
"wmv" { [BitConverter]::ToUInt64($chunkHeader, 16) }
default { 0 }
}
# Check for problems with chunk size
if ($chunkLength -le 0 -or $chunkLength -gt $remainingBytes) {
break
}
$position += $chunkLength
$lastValidChunkEnd = $position
}
$junkBytes = $fileSize - $lastValidChunkEnd
if ($junkBytes -gt 0) {
Write-Host " Trailing junk bytes found: $junkBytes"
}
else {
Write-Host " No trailing junk bytes found"
}
$fileStream.Close()
return $junkBytes
}
# Function to remove trailing junk bytes from the file
function Remove-TrailingJunkBytes($filePath, $junkBytes) {
$fileStream = [System.IO.File]::Open($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite)
$fileStream.SetLength($fileStream.Length - $junkBytes)
$fileStream.Close()
Write-Host " Truncated $filePath to $($fileStream.Length) bytes"
}
# Function to backup the trailing junk bytes to a file
function Backup-TrailingJunkBytes($filePath, $junkBytes) {
$backupFilePath = "$filePath.junk.txt"
$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
$junkStartIndex = $fileBytes.Length - $junkBytes
$junkEndIndex = $fileBytes.Length - 1
$junkBytes = $fileBytes[$junkStartIndex..$junkEndIndex]
[System.IO.File]::WriteAllBytes($backupFilePath, $junkBytes)
Write-Host " Backed up trailing junk bytes to $backupFilePath"
}
# Function to process a single file
function Invoke-FileCleanup($filePath, $autoRemove) {
# Check if the file exists
if (!(Test-Path $filePath -PathType Leaf)) {
Write-Host "The specified file does not exist: $filePath" -ForegroundColor $Host.PrivateData.WarningForegroundColor -BackgroundColor $Host.PrivateData.WarningBackgroundColor
Write-Host ""
return
}
Write-Host "`nProcessing $filePath"
$junkBytes = Find-TrailingJunkBytes $filePath
if ($junkBytes -eq -1) {
# Unsupported file type, skip further processing
return
}
if ($junkBytes -gt 256) {
Write-Host " No trailing junk bytes found in $filePath (false positive detected)" -ForegroundColor Green -BackgroundColor Black
Write-Host ""
return
}
if ($junkBytes -gt 0) {
Write-Host " Trailing junk bytes found: $junkBytes" -ForegroundColor $Host.PrivateData.WarningForegroundColor -BackgroundColor $Host.PrivateData.WarningBackgroundColor
if ($autoRemove) {
Backup-TrailingJunkBytes $filePath $junkBytes
Remove-TrailingJunkBytes $filePath $junkBytes
Write-Host " Removed trailing junk bytes from $filePath" -ForegroundColor Green -BackgroundColor Black
}
else {
$title = "Confirmation"
$message = "Do you want to remove the trailing junk bytes?"
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Remove the trailing junk bytes"
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Keep the trailing junk bytes"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
$result = $Host.UI.PromptForChoice($title, $message, $options, 1)
if ($result -eq 0) {
Backup-TrailingJunkBytes $filePath $junkBytes
Remove-TrailingJunkBytes $filePath $junkBytes
Write-Host " Removed trailing junk bytes from $filePath" -ForegroundColor Green -BackgroundColor Black
Write-Host ""
}
else {
Write-Host " Trailing junk bytes NOT removed from $filePath`n" -ForegroundColor $Host.PrivateData.ErrorForegroundColor -BackgroundColor $Host.PrivateData.ErrorBackgroundColor
}
}
}
else {
Write-Host " No trailing junk bytes found in $filePath" -ForegroundColor Green -BackgroundColor Black
Write-Host ""
}
}
# Check if the -help flag is provided
if ($args -contains "-help") {
Show-Help
exit 0
}
# Check if the -y flag is provided
$autoRemove = $args -contains "-y"
# Remove the -y flag from the arguments if present
$fileArgs = $args | Where-Object { $_ -ne "-y" }
# Check if file paths are provided as arguments (Direct mode)
if ($fileArgs.Count -gt 0) {
Write-Host "Video Cleaner 2.0.3"
foreach ($filePath in $fileArgs) {
Invoke-FileCleanup $filePath $autoRemove
}
}
elseif ($autoRemove) {
Write-Host "Please provide at least one file path along with the -y flag."
}
else {
# Prompt mode
Write-Host "Video Cleaner 2.0.3"
Write-Host "Use at your own risk. See help for details.`n"
while ($true) {
$userInput = Read-Host "Enter file path(s), 'q' to quit, or 'help'/'h' for more info"
if ($userInput -eq "q") {
break
}
elseif ($userInput -eq "help" -or $userInput -eq "h") {
Show-Help
continue
}
# Check if the -y flag is provided in the user input
$autoRemove = $userInput -split ' ' -contains "-y"
# Split the user input by spaces, except for quoted sections
$filePaths = $userInput | ForEach-Object {
$_ -split '(?<=^[^"]*(?:"[^"]*"[^"]*)*) (?=(?:[^"]*"[^"]*")*[^"]*$)'
} | Where-Object { $_ -ne "" -and $_ -ne "-y" }
foreach ($filePath in $filePaths) {
# Remove any surrounding quotes from the file path
$filePath = $filePath.Trim('"')
Invoke-FileCleanup $filePath $autoRemove
}
}
}
|
Technical Explanation
The Video Cleaner script is designed to detect and remove unnecessary or harmful bytes appended to the end of video files. This script supports MP4, MOV, and WMV video file formats.
File Type Detection
The script starts by detecting the file type of the input video file. It reads the first 16 bytes of the file, known as the file header, and analyzes the signature or magic bytes to determine the file type.
- For MP4 and MOV files, the script checks for the presence of specific chunk types such as "ftyp", "moov", "mdat", "free", or "skip" at the beginning of the file.
- For WMV files, the script checks for the ASCII representation of the byte sequence
0x30, 0x26, 0xb2, 0x75.
If the file type is not recognized, the script considers it an unsupported file type and skips further processing.
Chunk Size and Structure
Video files are typically composed of chunks or atoms, which are self-contained units of data. Each chunk has a specific structure depending on the file format.
- In MP4 and MOV files, each chunk consists of a 4-byte size field (in big-endian order) followed by a 4-byte chunk type identifier.
- In WMV files, each chunk consists of a 16-byte GUID (Globally Unique Identifier) followed by an 8-byte size field (in little-endian order).
The script determines the minimum chunk size based on the file type: 8 bytes for MP4 and MOV files, and 24 bytes for WMV files.
Detecting Trailing Junk Bytes
To detect trailing junk bytes, the script iterates through the chunks of the video file from the beginning to the end. It reads the chunk headers and calculates the expected chunk sizes based on the file format.
For each chunk, the script compares the remaining bytes in the file with the expected chunk size. If the remaining bytes are less than the minimum chunk size or if the chunk size is invalid (less than or equal to 0 or greater than the remaining bytes), the script considers the remaining bytes as trailing junk.
The script also handles cases where the file ends unexpectedly or if there is a small junk section at the end of the file.
Removing Trailing Junk Bytes
If trailing junk bytes are detected, the script provides an option to remove them. It prompts the user for confirmation before proceeding with the removal process.
To remove the trailing junk bytes, the script performs the following steps:
- It opens the video file using
[System.IO.File]::Open()with read and write access. - It truncates the file by setting its length to the original length minus the number of junk bytes using the
SetLength()method. - It closes the file stream.
Backup and Confirmation
Before removing the trailing junk bytes, the script creates a backup of the junk bytes in a separate file with the ".junk.txt" extension. This allows the user to review the removed bytes if needed.
The script prompts the user for confirmation before removing the trailing junk bytes. It provides an option to automatically remove the junk bytes without confirmation using the -y flag.