Compare commits

...

18 Commits

Author SHA1 Message Date
3dc94cbebe Update HEVC converter and NVEncoder 2022-09-20 19:30:04 +02:00
Michael Reber
2f9fdc681d update scripts to latest release 2020-10-05 19:54:16 +02:00
Michael Reber
2b71a0d860 Adding extra scripts for converting back to VR x264 support or 8K x264 2020-09-28 18:02:37 +02:00
Michael Reber
951f072b26 Speedup File check using 'HEVC' filename checking and other improvements 2020-07-02 16:09:11 +02:00
Michael Reber
a39cf40aa2 Update x265 encoder to newest version and fix review deletion 2020-06-20 01:16:45 +02:00
Michael Reber
24a6f024b1 Adding feature to override and review all corrupted files 2020-04-27 12:58:50 +02:00
Michael Reber
f7768b4a68 Adding review feature and one sec tolerance 2020-04-25 12:05:51 +02:00
Michael Reber
852bce9c61 Merge branch 'windows-hevc-converter' of 172.168.0.10:michael/video-in-place-hevc-converter into windows-hevc-converter 2020-04-15 16:38:22 +02:00
Michael Reber
b73a57e701 Increment convertedVideos variable to correct starting number 2020-04-15 16:37:38 +02:00
Michael Reber
5afb90a3ce Fix match error on upercase 'X' in string-reülacement and resulting self deletion 2020-04-09 15:39:52 +02:00
Michael Reber
0894aa74e2 Improving the description of how the converter is used 2020-04-03 13:13:10 +02:00
Michael Reber
3c91c0d78f Fix for LiteralPath in movie-files causing error 2020-03-25 23:27:09 +01:00
Michael Reber
92f7605c41 Adding a logging mechanism 2020-03-25 18:30:50 +01:00
Michael Reber
f66feb4451 Fix some stuff, better output, counting, and already converted visualisation 2020-03-25 12:06:48 +01:00
Michael Reber
3a33e96bb7 Update README.md and fix some stuff 2020-03-23 16:45:28 +01:00
Michael Reber
9ad464f618 Add codec checking, error-handling and automated file flushing 2020-03-23 14:50:22 +01:00
Michael Reber
3d5061d936 Rebuild script with NVEnc for faster transcoding 2020-03-06 00:43:30 +01:00
Michael Reber
c95c1f38e0 Upload Windows Converter 2020-03-05 20:48:33 +01:00
24 changed files with 1469 additions and 254 deletions

View File

@ -1,3 +0,0 @@
source 'https://rubygems.org'
gem 'streamio-ffmpeg'
gem 'peach'

View File

@ -1,56 +1,41 @@
# Convert videos in-place to h265 (HEVC)
# Automated HEVC-Video-Converter
This is a simple ruby script that will chuch through a directory converting all of the videos to use h.265 or HEVC in place. This works by converting the video file to \*.tmp.mp4, and then moving it to its original file name with the mp4 extension. It will delete the old version of the file.
A PowerShell script for converting video to the HEVC video format using GPU hardware acceleration with NVEnc for Windows.
The main advantage is that you can (usually) save a lot of disk space this way.
## Usage
![image](/attachments/985e0fa6-ec4a-4ad0-a1c7-d70f59e7a23d)
This is a long running ruby script, it makes calls to FFMPEG using a ruby gem to scrape metadata of videos, and transcode them. It works by appling some simple filters to create a list of videos that can be converted, and then works through that queue.
## Space Saving Examples
Change to the directory, where the config and script is located then run: `$ ruby convertVideos.rb start`
- 2.5GB MP4 to 500MB HEVC MP4
- 3GB MP4 to 800MB HEVC MP4
_Results vary and depend on the input video's format, bitrate etc._
## Example Config
Please note the preceding colons are important. Also the file must be called config.yml
## Minimum System Requirements
```
:directory: /mnt/movies
:min_age_days: 5
:log_location: /home/user/logs/hevc_conversion.log
:preset: slow
:threads: 8
:max_new_file_size_ratio: 0.9
```
- Computer with at least two CPU cores and 4GB Ram
- Recent nvidia graphics card ([see: NVENC support matrix](https://developer.nvidia.com/video-encode-decode-gpu-support-matrix))
- Latest nvidia graphics drivers
| Configuration | Description |
| -------- | -------- |
| directory | The directory to recurse into. All files will be considered within that directory. |
|min_age_days | How many days old does this file have to be to be considered for conversion. |
|log_location| This Script is designed to run deetached in the background. As such the log location is the best way to figure out whatis going on and the status of the conversion |
| preset | used to trade off between final file size, quality, and transcoding time. I recomend slow. See ffmpeg docs for more detail. |
|threads | How many threads should be used for converting the file. |
|max_new_file_size_ratio| Once transocing an individual file is finished, this script will make sure the output is smaller than the origional. Spesifically new file size <= Old file size * this value. Since transcoding always involves quality loss this value should be less than 1.0 |
## Encoding
During encoding (conversion), high **CPU** and **GPU** usage is normal. Make sure that you only run the script if no other graphics processes are running on the computer. (Example computer games)
## Coridnation
When this script starts to convert a video, it creates a .filename.tmp.mp4 file that used the old files filename. This acts as kind of a lock because the exitsance of that file is checked before conversion. This also allows us to save state between runs without needing to share a database or other coridination servcie. That file is left behind if, for any reason, the conversion fails, the process is stopped, or if afterthe conversion the new HEVC file is not at least 10% smaller than the origional. The content is replace with an explination if possible.
The script converts the files next to the original with '-HEVC' at the end or by replacing the string x264 (old) in the file name with x265.
After conversion, a test of the new video file is performed to ensure its integrity.
If this test is successful, the old file is deleted.
If not, the failed conversion file is deleted and logged.
It also makes it so multiple computers can run this script, provided they are all run against the same backing file system (SMB, NFS, etc.). There is a risk of duplicate work if two processes try to start to transcode the same file at the same time, but this risk is minimal for large libraries. To further mitigate, the list of candidate files is randomaized.
## Script Usage
1. Clone this repository on your computer
2. Download the latest NVEnc for Windows: https://github.com/rigaya/NVEnc/releases
3. Extract the files to "video-in-place-hevc-converter\encoder\" and replace the old files.
4. Edit the $videoPath variable in the PowerShell file to point to the folder of your "video files" to convert. (**ATTENTION: The ending-slash must be preserved in the path specification!**)
5. Execute **convert_Videos.ps1** to convert the video files under the specified path to HEVC.
## Setup
## Extra Content (\_extras folder)
1. Instal Ruby 2.1.0+
2. Install (ffmpeg)[https://ffmpeg.org/download.html] near version 3.4.4 `# apt-get install ffmpeg`
3. `$ gem install bundler`
4. `$ bundle install`
5. Optional: Install screen or tmux. This is to allows it to run in the background after closing SSH on a server.
6. Edit the script if you want.
7. Run the script.
8. Automate it e.g. with cron?
## Disclaimers
- Only use with videos you have the rights to copy
- This will delete the original video, so use with care. Test with a test directory before running on your entire library.
- Use a test file with all your media playing devices to ensure that they can handle HEVC encoding.
- **manually_review_videos.ps1** - Redoes the review process using the "review_needed.log" -> before useing set the "$logFolderName" variable in file.

View File

@ -0,0 +1,304 @@
# Converts videos back to x264 for given path: (Video resolution is same as source up to 8K)
# Converter: https://github.com/rigaya/NVEnc/releases
# Forces a higher resolution with ffmpeg than actually intended for the x264 standard.
# Workaround to createn non-standart 8K x254 videos.
#-----------------------------------------------------------------------
# Edit the $videoPath variable to point to your video-files folder:
$videoPath = 'Z:\__VR_SP_CONTENT\_data'
#-----------------------------------------------------------------------
$forceReviewAll = $false # If set to $true all failed conversions are reviewed even those that are obviously corrupt.
$ffmpegBin = "$PSScriptRoot\ffmpeg\ffmpeg.exe"
$fileTester = "$PSScriptRoot\..\mediainfo.exe"
$reviewPlayer = "C:\Program Files\VideoLAN\VLC\vlc.exe"
#############################################################################################################
# Testing and conversion preparations #
#############################################################################################################
if(!(Test-Path $ffmpegBin -PathType leaf))
{
Write-Host "ffmpeg.exe not found, please check path in `"$ffmpegBin`"." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
# Get video list from provided path:
$videos = Get-ChildItem -LiteralPath $videoPath -Name -Recurse -Include ('*.mp4', '*.mkv') #('*.avi', '*.mp4', '*.mkv') - avi currently doesn't work.
# Instantiating used variables:
$videoID = 1
$convertedVideos = 0
$notConvertedVideos = 0
$failedVideos = 0
# Square brackets are used as wildcards in Powershell.
# To recognize this as normal text, the LogWite function uses the -LiteralPath parameter instead of the -Path parameter.
$logFolderName = $((Get-Date).tostring("yyyy-MM-dd") + ' - [run started at ' + (Get-Date).tostring("HH-mm") + ']')
$count = $videos.Count
if($videos.Count -lt 1)
{
Write-Host "No videos found in: $videoPath" -ForegroundColor Red
Read-Host -Prompt "Press Enter to exit"
exit
}
# Log write function:
Function LogWrite
{
Param ([string]$logstring)
Add-content -LiteralPath $Logfile -value $logstring
}
# Create new log directory:
New-Item -itemType Directory -Path $PSScriptRoot\..\logs\ -Name $logFolderName > $null
# Calculates the total size of the given directory in GB:
Write-Host "Inizialization in progress.."
$folderSizeInGB = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
Clear-Host # Clears the upper message from the window
#############################################################################################################
# END - Testing and conversion preparations
#############################################################################################################
# Main conversion process #
#############################################################################################################
Write-Host "Found $count videos with a total size of $folderSizeInGB - starting converter.." -ForegroundColor Cyan
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
foreach($video in $videos)
{
$video = $video.Replace("`"", "")
$inputFile = $videoPath + "\" + $video
# Pre-file-check if "HEVC" exist in filename skip filechecking:
if ($video -match "HEVC")
{
$codec = "HEVC"
} else {
# Get and check File info for codec, if it's already HEVC: - JSON query: (['media']['track'][1]['Format'])
$fileDetails = cmd /c $fileTester $inputFile --Output=JSON | ConvertFrom-Json
$codec = $fileDetails.media.track[1].Format
}
# If not already HEVC, convert video using NVEncC64:
if($codec -eq "HEVC")
{
# Configure new file naming:
if ($video -match "265")
{
$outputFile = $videoPath + "\" + $video.Replace("265", "264")
} else {
$outputFile = $videoPath + "\" + $video.Insert(($video.Length - 4), '-x264')
}
Write-Host "Analyzing video ($videoID of $count), starting convertion of number: $($convertedVideos + 1) please wait.." -ForegroundColor Magenta
Write-Host "$video `nto:`n$outputFile" -ForegroundColor White
Write-Host
# If the Subtitles should not be copied, delete the "--sub-copy 1,2" argument.
#$arguments = "--input `"$inputFile`" --codec AVC --audio-copy 1,2,3,4 --sub-copy 1,2,3,4 --profile $profile --output `"$outputFile`""
$arguments = "-i `"$inputFile`" -c:v libx264 -preset fast -crf 18 -c:a copy -map 0 -map_metadata 0 -movflags +faststart -pix_fmt yuv420p `"$outputFile`""
Start-Process $ffmpegBin -ArgumentList $arguments -WindowStyle Minimized
#$processName = 'NVEncC64'
$processName = 'ffmpeg'
Start-Sleep -Seconds 1
# Sets to use 3 cores (always set to one less core that your CPU has)
# 2 Cores = 3, 3 Cores = 7, 4 cores = 15, 5 cores = 31, 6 cores = 63
# Code to calculate for your CPU:
# $noOfCores = Get-WmiObject Win32_Processor | Measure-Object NumberOfLogicalProcessors -Sum
# $noOfCores.Sum = $noOfCores.Sum - 1
# [math]::Pow(2,$($noOfCores).Sum) - 1
#
$process = Get-Process $processName; $process.ProcessorAffinity=7
Start-Sleep -Seconds 2
# Sets priorty to High
# Values: High, AboveNormal, Normal, BelowNormal, Low
$process = Get-Process -Id $process.Id
$process.PriorityClass = 'High'
# Waits for process to complete
$processID = (Get-Process $processName).id
Wait-Process -Id $processID
if(Test-Path -LiteralPath $outputFile)
# Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed.
#No characters are interpreted as wildcard characters.
{
Write-Host "CONVERSION DONE! FILE: `"$outputFile`" FOUND" -ForegroundColor Yellow
Start-Sleep -Seconds 4
# Check if File is valid; the duration of video must be the same! (['media']['track'][0]['Duration']):
$fileDetails_new = cmd /c $fileTester $outputFile --Output=JSON | ConvertFrom-Json
# This code gets the duration and splits it in two parts, only the part before the "." is needed:
$StreamSize_old,$notused = $($fileDetails.media.track[0].Duration).split('.')
$StreamSize_new,$notused = $($fileDetails_new.media.track[0].Duration).split('.')
Write-Host
Write-Host "Old Duration was: $StreamSize_old `nnew Duration is: $StreamSize_new" -ForegroundColor White
Write-Host
if ($StreamSize_new -eq $StreamSize_old)
{
# Delete old video File!
Write-Host "Conversion Successful! - Deleting old file.."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} else
{
if (($StreamSize_new -eq $($StreamSize_old - 1)) -or ($($StreamSize_new - 1) -eq $StreamSize_old))
{
# Newly converted file does not match exactly the old duration. But diffrence its not more than 1 sec!
Write-Host "Conversion Done! - Streamsize is not exactly the same. But still OK - Ignoring!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} elseif (($StreamSize_new -ge $($StreamSize_old - 20)) -or ($($StreamSize_new - 20) -ge $StreamSize_old))
{
# New converted file has been converted but with up to max 20 seconds deviation!
Write-Host "Conversion Done! - But streamsize is not the same. - Please review!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
if ($forceReviewAll)
{
# Mark probably corrupted file for review
Write-Host "Conversion Failed! - Marked for review! because it is desired."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
# Delete obviously corrupt File!
Write-Host "Conversion Failded! - Deleting new converted file.." -ForegroundColor Red
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $outputFile
$failedVideos = $failedVideos + 1
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\error_during_conversion.log"
LogWrite "$inputFile,$StreamSize_new,$StreamSize_old,"
}
}
}
}
} else
{
# Alert if video doesn't need conversion:
Write-Host "Analyzing video ($videoID of $count) - skip" -ForegroundColor Magenta
Write-Host "$video is already in correct format!" -ForegroundColor Green
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
# Increments not converted video counter:
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\skipped_files.log"
LogWrite "$inputFile"
}
# Increments video counter:
$videoID = $videoID + 1
$outputFile = "" # Resets the variable to nothing, that in case of an error the old file is not affected.
}
#############################################################################################################
# END - Main conversion process
#############################################################################################################
# Evaluation part of the conversion script #
#############################################################################################################
if ($review)
{
$reviewFiles = Get-content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log" | Measure-Object Line
$ReviewCount = $reviewFiles.Lines
Write-Host "There are $ReviewCount files to review.." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to start reviewing first file"
Write-Host
Write-Host "################################################################################################"
$review_count = 1
foreach($review_pair in Get-Content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log")
{
$oldFile,$newFile = $review_pair.split(',')
Write-Host "Playing $review_count converted file: $newFile"
Write-Host
# Starting VLC Player with the file to review
Start-Process $reviewPlayer -ArgumentList `"$newFile`"
Start-Sleep -Seconds 6
# Wait until player is closed.
$processID = (Get-Process "vlc").id
Wait-Process -Id $processID
$msg = 'Do you want to keep the newly converted file and delete the old? (N for deleting NEW-file) [Y/N]'
do {
$response = Read-Host -Prompt $msg
} until (($response -eq 'n') -or ($response -eq 'y'))
if ($response -eq 'y')
{
Write-Host "delete old file, keep newly converted.."
$to_delete = $oldFile
} else
{
Write-Host "delete newly converted, keep old file"
$to_delete = $newFile
$failedVideos = $failedVideos + 1
$convertedVideos = $convertedVideos - 1
}
Write-Host "DELETED: $to_delete" -ForegroundColor Red
Remove-Item -LiteralPath $to_delete
Write-Host "------------------------------------------------------------------------------------------------"
Start-Sleep -Seconds 2
}
}
Write-Host "Final calculations are in progress.."
$folderSizeInGB2 = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
if($notConvertedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos." -ForegroundColor Green
} else
{
if($failedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because of already correct codec!" -ForegroundColor Green
} else
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because error or already correct codec!!" -ForegroundColor Green
Write-Host "$failedVideos have failed!" -ForegroundColor Red
}
}
Write-Host "Directory size before conversion: $folderSizeInGB"
Write-Host "Directory size after conversion: $folderSizeInGB2"
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

View File

@ -0,0 +1,326 @@
# Converts videos back to x264 for VR playback for given path:
# Converter: https://github.com/rigaya/NVEnc/releases
# Maximum VR Video Dimensions for Oculus Go are: 3840x2048 pixel!! Higher resolution ends with blackscreen!
# Maximum resolution for x264 codec is = 4096x4096 pixel!
#-----------------------------------------------------------------------
# Edit the $videoPath variable to point to your video-files folder:
$videoPath = 'Z:\__VR_SP_CONTENT\_data'
#-----------------------------------------------------------------------
$debug = $true
$forceReviewAll = $false # If set to $true all failed conversions are reviewed even those that are obviously corrupt.
$forceReconvertAllToMatchVRresolution = $true; # only x264 videos AND videos not bigger than 3840x2048 pixel are playable on the Oculus Go.
$NVEncoder = "$PSScriptRoot\..\encoder\NVEncC64.exe"
$fileTester = "$PSScriptRoot\..\mediainfo.exe"
$reviewPlayer = "C:\Program Files\VideoLAN\VLC\vlc.exe"
#############################################################################################################
# Testing and conversion preparations #
#############################################################################################################
if(!(Test-Path $NVEncoder -PathType leaf))
{
Write-Host "NVEncC64.exe not found, please check path in `"$NVEncoder`"." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
# Get video list from provided path:
$videos = Get-ChildItem -LiteralPath $videoPath -Name -Recurse -Include ('*.mp4', '*.mkv') #('*.avi', '*.mp4', '*.mkv') - avi currently doesn't work.
# Instantiating used variables:
$videoID = 1
$convertedVideos = 0
$notConvertedVideos = 0
$failedVideos = 0
# Square brackets are used as wildcards in Powershell.
# To recognize this as normal text, the LogWite function uses the -LiteralPath parameter instead of the -Path parameter.
$logFolderName = $((Get-Date).tostring("yyyy-MM-dd") + ' - [run started at ' + (Get-Date).tostring("HH-mm") + ']')
$count = $videos.Count
if($videos.Count -lt 1)
{
Write-Host "No videos found in: $videoPath" -ForegroundColor Red
Read-Host -Prompt "Press Enter to exit"
exit
}
# Log write function:
Function LogWrite
{
Param ([string]$logstring)
Add-content -LiteralPath $Logfile -value $logstring
}
# Create new log directory:
New-Item -itemType Directory -Path $PSScriptRoot\..\logs\ -Name $logFolderName > $null
# Calculates the total size of the given directory in GB:
Write-Host "Inizialization in progress.."
$folderSizeInGB = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
Clear-Host # Clears the upper message from the window
#############################################################################################################
# END - Testing and conversion preparations
#############################################################################################################
# Main conversion process #
#############################################################################################################
Write-Host "Found $count videos with a total size of $folderSizeInGB - starting converter.." -ForegroundColor Cyan
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
foreach($video in $videos)
{
$video = $video.Replace("`"", "")
$inputFile = $videoPath + "\" + $video
# Get and check File info for codec and resolution, if it's already HEVC: - JSON query: (['media']['track'][1]['Format'])
$fileDetails = cmd /c $fileTester $inputFile --Output=JSON | ConvertFrom-Json
$codec = $fileDetails.media.track[1].Format
$reconvert = $false
$resolution_width = $fileDetails.media.track[1].Width
$resolution_height = $fileDetails.media.track[1].Height
if($resolution_width -gt '3840') # max resolution for x264 = 4096
{
if ($forceReconvertAllToMatchVRresolution)
{
$reconvert = $true
}
$new_resolution_width = '3840'
$new_resolution_height = $resolution_height / $resolution_width * $new_resolution_width
if ($new_resolution_height -gt '2048')
{
$new_resolution_width = $new_resolution_width / $new_resolution_height * '2048'
$new_resolution_height = '2048'
}
}
# If not already x264 or reconvert, converting video using NVEncC64:
if(($codec -eq "HEVC") -or ($reconvert))
{
# Configure new file naming:
if ($video -match "265")
{
$outputFile = $videoPath + "\" + $video.Replace("265", "264")
} else {
$outputFile = $videoPath + "\" + $video.Insert(($video.Length - 4), '-x264')
}
Write-Host "Analyzing video ($videoID of $count), starting convertion of number: $($convertedVideos + 1) please wait.." -ForegroundColor Magenta
Write-Host "$video `nto:`n$outputFile" -ForegroundColor White
Write-Host
# If the Subtitles should not be copied, delete the "--sub-copy 1,2" argument.
$arguments = "--input `"$inputFile`" --codec AVC --audio-copy 1,2,3,4 --sub-copy 1,2,3,4 --profile high --output-res $($new_resolution_width)x$($new_resolution_height) --output `"$outputFile`""
if ($debug)
{
Write-Host "Starting conversion of next file with the following command:"
Write-Host "$NVEncoder $arguments" -ForegroundColor Green
}
Start-Process $NVEncoder -ArgumentList $arguments -WindowStyle Minimized
$processName = 'NVEncC64'
Start-Sleep -Seconds 1
# Sets to use 3 cores (always set to one less core that your CPU has)
# 2 Cores = 3, 3 Cores = 7, 4 cores = 15, 5 cores = 31, 6 cores = 63
# Code to calculate for your CPU:
# $noOfCores = Get-WmiObject Win32_Processor | Measure-Object NumberOfLogicalProcessors -Sum
# $noOfCores.Sum = $noOfCores.Sum - 1
# [math]::Pow(2,$($noOfCores).Sum) - 1
#
$process = Get-Process $processName; $process.ProcessorAffinity=7
Start-Sleep -Seconds 2
# Sets priorty to High
# Values: High, AboveNormal, Normal, BelowNormal, Low
$process = Get-Process -Id $process.Id
$process.PriorityClass = 'High'
# Waits for process to complete
$processID = (Get-Process $processName).id
Wait-Process -Id $processID
if(Test-Path -LiteralPath $outputFile)
# Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed.
#No characters are interpreted as wildcard characters.
{
Write-Host "CONVERSION DONE! FILE: `"$outputFile`" FOUND" -ForegroundColor Yellow
Start-Sleep -Seconds 4
# Check if File is valid; the duration of video must be the same! (['media']['track'][0]['Duration']):
$fileDetails_new = cmd /c $fileTester $outputFile --Output=JSON | ConvertFrom-Json
# This code gets the duration and splits it in two parts, only the part before the "." is needed:
$StreamSize_old,$notused = $($fileDetails.media.track[0].Duration).split('.')
$StreamSize_new,$notused = $($fileDetails_new.media.track[0].Duration).split('.')
Write-Host
Write-Host "Old Duration was: $StreamSize_old `nnew Duration is: $StreamSize_new" -ForegroundColor White
Write-Host
if ($StreamSize_new -eq $StreamSize_old)
{
# Delete old video File!
Write-Host "Conversion Successful! - Deleting old file.."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} else
{
if (($StreamSize_new -eq $($StreamSize_old - 1)) -or ($($StreamSize_new - 1) -eq $StreamSize_old))
{
# Newly converted file does not match exactly the old duration. But diffrence its not more than 1 sec!
Write-Host "Conversion Done! - Streamsize is not exactly the same. But still OK - Ignoring!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} elseif (($StreamSize_new -ge $($StreamSize_old - 20)) -or ($($StreamSize_new - 20) -ge $StreamSize_old))
{
# New converted file has been converted but with up to max 20 seconds deviation!
Write-Host "Conversion Done! - But streamsize is not the same. - Please review!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
if ($forceReviewAll)
{
# Mark probably corrupted file for review
Write-Host "Conversion Failed! - Marked for review! because it is desired."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
# Delete obviously corrupt File!
Write-Host "Conversion Failded! - Deleting new converted file.." -ForegroundColor Red
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $outputFile
$failedVideos = $failedVideos + 1
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\error_during_conversion.log"
LogWrite "$inputFile,$StreamSize_new,$StreamSize_old,"
}
}
}
}
} else
{
# Alert if video doesn't need conversion:
Write-Host "Analyzing video ($videoID of $count) - skip" -ForegroundColor Magenta
Write-Host "$video is already in correct format!" -ForegroundColor Green
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
# Increments not converted video counter:
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\skipped_files.log"
LogWrite "$inputFile"
}
# Increments video counter:
$videoID = $videoID + 1
$outputFile = "" # Resets the variable to nothing, that in case of an error the old file is not affected.
}
#############################################################################################################
# END - Main conversion process
#############################################################################################################
# Evaluation part of the conversion script #
#############################################################################################################
if ($review)
{
$reviewFiles = Get-content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log" | Measure-Object Line
$ReviewCount = $reviewFiles.Lines
Write-Host "There are $ReviewCount files to review.." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to start reviewing first file"
Write-Host
Write-Host "################################################################################################"
$review_count = 1
foreach($review_pair in Get-Content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log")
{
$oldFile,$newFile = $review_pair.split(',')
Write-Host "Playing $review_count converted file: $newFile"
Write-Host
# Starting VLC Player with the file to review
Start-Process $reviewPlayer -ArgumentList `"$newFile`"
Start-Sleep -Seconds 6
# Wait until player is closed.
$processID = (Get-Process "vlc").id
Wait-Process -Id $processID
$msg = 'Do you want to keep the newly converted file and delete the old? (N for deleting NEW-file) [Y/N]'
do {
$response = Read-Host -Prompt $msg
} until (($response -eq 'n') -or ($response -eq 'y'))
if ($response -eq 'y')
{
Write-Host "delete old file, keep newly converted.."
$to_delete = $oldFile
} else
{
Write-Host "delete newly converted, keep old file"
$to_delete = $newFile
$failedVideos = $failedVideos + 1
$convertedVideos = $convertedVideos - 1
}
Write-Host "DELETED: $to_delete" -ForegroundColor Red
Remove-Item -LiteralPath $to_delete
Write-Host "------------------------------------------------------------------------------------------------"
Start-Sleep -Seconds 2
}
}
Write-Host "Final calculations are in progress.."
$folderSizeInGB2 = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
if($notConvertedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos." -ForegroundColor Green
} else
{
if($failedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because of already correct codec!" -ForegroundColor Green
} else
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because error or already correct codec!!" -ForegroundColor Green
Write-Host "$failedVideos have failed!" -ForegroundColor Red
}
}
Write-Host "Directory size before conversion: $folderSizeInGB"
Write-Host "Directory size after conversion: $folderSizeInGB2"
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

View File

@ -0,0 +1,332 @@
# Converts videos back to x264 for VR playback for given path:
# Converter: https://github.com/FFmpeg/FFmpeg
# Maximum VR Video Dimensions for Oculus Go are: 3840x2048 pixel!! Higher resolution ends with blackscreen!
# Maximum resolution for x264 codec is = 4096x4096 pixel!
#-----------------------------------------------------------------------
# Edit the $videoPath variable to point to your video-files folder:
$videoPath = 'Z:\__VR_SP_CONTENT\__TODO'
#-----------------------------------------------------------------------
$debug = $true
$forceReviewAll = $false # If set to $true all failed conversions are reviewed even those that are obviously corrupt.
$forceReconvertAllToMatchVRresolution = $true; # only x264 videos AND videos not bigger than 3840x2048 pixel are playable on the Oculus Go.
$ffmpegBin = "$PSScriptRoot\ffmpeg\ffmpeg.exe"
$fileTester = "$PSScriptRoot\..\mediainfo.exe"
$reviewPlayer = "C:\Program Files\VideoLAN\VLC\vlc.exe"
#############################################################################################################
# Testing and conversion preparations #
#############################################################################################################
if(!(Test-Path $ffmpegBin -PathType leaf))
{
Write-Host "ffmpeg.exe not found, please check path in `"$ffmpegBin`"." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
# Get video list from provided path:
$videos = Get-ChildItem -LiteralPath $videoPath -Name -Recurse -Include ('*.mp4', '*.mkv') #('*.avi', '*.mp4', '*.mkv') - avi currently doesn't work.
# Instantiating used variables:
$videoID = 1
$convertedVideos = 0
$notConvertedVideos = 0
$failedVideos = 0
# Square brackets are used as wildcards in Powershell.
# To recognize this as normal text, the LogWite function uses the -LiteralPath parameter instead of the -Path parameter.
$logFolderName = $((Get-Date).tostring("yyyy-MM-dd") + ' - [run started at ' + (Get-Date).tostring("HH-mm") + ']')
$count = $videos.Count
if($videos.Count -lt 1)
{
Write-Host "No videos found in: $videoPath" -ForegroundColor Red
Read-Host -Prompt "Press Enter to exit"
exit
}
# Log write function:
Function LogWrite
{
Param ([string]$logstring)
Add-content -LiteralPath $Logfile -value $logstring
}
# Create new log directory:
New-Item -itemType Directory -Path $PSScriptRoot\..\logs\ -Name $logFolderName > $null
# Calculates the total size of the given directory in GB:
Write-Host "Inizialization in progress.."
$folderSizeInGB = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
Clear-Host # Clears the upper message from the window
#############################################################################################################
# END - Testing and conversion preparations
#############################################################################################################
# Main conversion process #
#############################################################################################################
Write-Host "Found $count videos with a total size of $folderSizeInGB - starting converter.." -ForegroundColor Cyan
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
foreach($video in $videos)
{
$video = $video.Replace("`"", "")
$inputFile = $videoPath + "\" + $video
# Get and check File info for codec and resolution, if it's already HEVC: - JSON query: (['media']['track'][1]['Format'])
$fileDetails = cmd /c $fileTester $inputFile --Output=JSON | ConvertFrom-Json
$codec = $fileDetails.media.track[1].Format
$reconvert = $false
$resolution_width = $fileDetails.media.track[1].Width
$resolution_height = $fileDetails.media.track[1].Height
if($resolution_width -gt '3840') # max resolution for x264 = 4096
{
if ($forceReconvertAllToMatchVRresolution)
{
$reconvert = $true
}
$new_resolution_width = '3840'
$new_resolution_height = $resolution_height / $resolution_width * $new_resolution_width
if ($new_resolution_height -gt '2048')
{
$new_resolution_width = $new_resolution_width / $new_resolution_height * '2048'
$new_resolution_height = '2048'
}
}
# If not already x264 or reconvert, converting video using ffmpeg:
if(($codec -eq "HEVC") -or ($reconvert))
{
# Configure new file naming:
if ($video -match "265")
{
$outputFile = $videoPath + "\" + $video.Replace("265", "264")
} else {
$outputFile = $videoPath + "\" + $video.Insert(($video.Length - 4), '-x264')
}
Write-Host "Analyzing video ($videoID of $count), starting convertion of number: $($convertedVideos + 1) please wait.." -ForegroundColor Magenta
Write-Host "$video `nto:`n$outputFile" -ForegroundColor White
Write-Host
$rescale = ""
if ($reconvert)
{
$rescale = "-s $($new_resolution_width)x$($new_resolution_height)"
}
# If the Subtitles should not be copied, delete the "--sub-copy 1,2" argument.
$arguments = "-i `"$inputFile`" -c:v libx264 -preset fast -crf 18 -c:a copy -map 0 -map_metadata 0 -movflags +faststart -pix_fmt yuv420p $rescale `"$outputFile`""
if ($debug)
{
Write-Host "Starting conversion of next file with the following command:"
Write-Host "$ffmpegBin $arguments" -ForegroundColor Green
}
Start-Process $ffmpegBin -ArgumentList $arguments -WindowStyle Minimized
$processName = 'ffmpeg'
Start-Sleep -Seconds 1
# Sets to use 3 cores (always set to one less core that your CPU has)
# 2 Cores = 3, 3 Cores = 7, 4 cores = 15, 5 cores = 31, 6 cores = 63
# Code to calculate for your CPU:
$noOfCores = Get-WmiObject Win32_Processor | Measure-Object NumberOfLogicalProcessors -Sum
$noOfCores.Sum = $noOfCores.Sum - 1
[Int]$processorAffinity = [math]::Pow(2,$($noOfCores).Sum) - 1
#
$process = Get-Process $processName; $process.ProcessorAffinity=$processorAffinity
#Start-Sleep -Seconds 2
# Sets priorty to High
# Values: High, AboveNormal, Normal, BelowNormal, Low
$process = Get-Process -Id $process.Id
$process.PriorityClass = 'High'
# Waits for process to complete
$processID = (Get-Process $processName).id
Wait-Process -Id $processID
if(Test-Path -LiteralPath $outputFile)
# Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed.
#No characters are interpreted as wildcard characters.
{
Write-Host "CONVERSION DONE! FILE: `"$outputFile`" FOUND" -ForegroundColor Yellow
Start-Sleep -Seconds 4
# Check if File is valid; the duration of video must be the same! (['media']['track'][0]['Duration']):
$fileDetails_new = cmd /c $fileTester $outputFile --Output=JSON | ConvertFrom-Json
# This code gets the duration and splits it in two parts, only the part before the "." is needed:
$StreamSize_old,$notused = $($fileDetails.media.track[0].Duration).split('.')
$StreamSize_new,$notused = $($fileDetails_new.media.track[0].Duration).split('.')
Write-Host
Write-Host "Old Duration was: $StreamSize_old `nnew Duration is: $StreamSize_new" -ForegroundColor White
Write-Host
if ($StreamSize_new -eq $StreamSize_old)
{
# Delete old video File!
Write-Host "Conversion Successful! - Deleting old file.."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} else
{
if (($StreamSize_new -eq $($StreamSize_old - 1)) -or ($($StreamSize_new - 1) -eq $StreamSize_old))
{
# Newly converted file does not match exactly the old duration. But diffrence its not more than 1 sec!
Write-Host "Conversion Done! - Streamsize is not exactly the same. But still OK - Ignoring!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
} elseif (($StreamSize_new -ge $($StreamSize_old - 20)) -or ($($StreamSize_new - 20) -ge $StreamSize_old))
{
# New converted file has been converted but with up to max 20 seconds deviation!
Write-Host "Conversion Done! - But streamsize is not the same. - Please review!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
if ($forceReviewAll)
{
# Mark probably corrupted file for review
Write-Host "Conversion Failed! - Marked for review! because it is desired."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
} else
{
# Delete obviously corrupt File!
Write-Host "Conversion Failded! - Deleting new converted file.." -ForegroundColor Red
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $outputFile
$failedVideos = $failedVideos + 1
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\error_during_conversion.log"
LogWrite "$inputFile,$StreamSize_new,$StreamSize_old,"
}
}
}
}
} else
{
# Alert if video doesn't need conversion:
Write-Host "Analyzing video ($videoID of $count) - skip" -ForegroundColor Magenta
Write-Host "$video is already in correct format!" -ForegroundColor Green
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
# Increments not converted video counter:
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\..\logs\$logFolderName\skipped_files.log"
LogWrite "$inputFile"
}
# Increments video counter:
$videoID = $videoID + 1
$outputFile = "" # Resets the variable to nothing, that in case of an error the old file is not affected.
}
#############################################################################################################
# END - Main conversion process
#############################################################################################################
# Evaluation part of the conversion script #
#############################################################################################################
if ($review)
{
$reviewFiles = Get-content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log" | Measure-Object Line
$ReviewCount = $reviewFiles.Lines
Write-Host "There are $ReviewCount files to review.." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to start reviewing first file"
Write-Host
Write-Host "################################################################################################"
$review_count = 1
foreach($review_pair in Get-Content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log")
{
$oldFile,$newFile = $review_pair.split(',')
Write-Host "Playing $review_count converted file: $newFile"
Write-Host
# Starting VLC Player with the file to review
Start-Process $reviewPlayer -ArgumentList `"$newFile`"
Start-Sleep -Seconds 6
# Wait until player is closed.
$processID = (Get-Process "vlc").id
Wait-Process -Id $processID
$msg = 'Do you want to keep the newly converted file and delete the old? (N for deleting NEW-file) [Y/N]'
do {
$response = Read-Host -Prompt $msg
} until (($response -eq 'n') -or ($response -eq 'y'))
if ($response -eq 'y')
{
Write-Host "delete old file, keep newly converted.."
$to_delete = $oldFile
} else
{
Write-Host "delete newly converted, keep old file"
$to_delete = $newFile
$failedVideos = $failedVideos + 1
$convertedVideos = $convertedVideos - 1
}
Write-Host "DELETED: $to_delete" -ForegroundColor Red
Remove-Item -LiteralPath $to_delete
Write-Host "------------------------------------------------------------------------------------------------"
Start-Sleep -Seconds 2
}
}
Write-Host "Final calculations are in progress.."
$folderSizeInGB2 = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
if($notConvertedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos." -ForegroundColor Green
} else
{
if($failedVideos -eq 0)
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because of already correct codec!" -ForegroundColor Green
} else
{
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because error or already correct codec!!" -ForegroundColor Green
Write-Host "$failedVideos have failed!" -ForegroundColor Red
}
}
Write-Host "Directory size before conversion: $folderSizeInGB"
Write-Host "Directory size after conversion: $folderSizeInGB2"
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

View File

@ -0,0 +1,115 @@
# Converts videos to HEVC for given path:
# https://github.com/rigaya/NVEnc/releases
#-----------------------------------------------------------------------
# Edit the $videoPath variable to point to your video-files folder:
$videoPath = 'Z:\__VR_SP_CONTENT\_data'
#-----------------------------------------------------------------------
$fileTester = "$PSScriptRoot\..\mediainfo.exe"
#############################################################################################################
# Testing and conversion preparations #
#############################################################################################################
# Get video list from provided path:
$videos = Get-ChildItem -LiteralPath $videoPath -Name -Recurse -Include ('*.mp4', '*.mkv') #('*.avi', '*.mp4', '*.mkv') - avi currently doesn't work.
# Instantiating used variables:
$videoID = 1
$convertedVideos = 0
$notConvertedVideos = 0
# Square brackets are used as wildcards in Powershell.
# To recognize this as normal text, the LogWite function uses the -LiteralPath parameter instead of the -Path parameter.
$logFolderName = $((Get-Date).tostring("yyyy-MM-dd") + ' - [preList - convert HEVC to x264 created at ' + (Get-Date).tostring("HH-mm") + ']')
$count = $videos.Count
if($videos.Count -lt 1)
{
Write-Host "No videos found in: $videoPath" -ForegroundColor Red
Read-Host -Prompt "Press Enter to exit"
exit
}
# Log write function:
Function LogWrite
{
Param ([string]$logstring)
Add-content -LiteralPath $Logfile -value $logstring
}
# Create new log directory:
New-Item -itemType Directory -Path $PSScriptRoot\ -Name $logFolderName > $null
Clear-Host # Clears the upper message from the window
#############################################################################################################
# END - Testing and conversion preparations
#############################################################################################################
# Main conversion process #
#############################################################################################################
Write-Host "Found $count videos with a total size of $folderSizeInGB - starting generation of list.." -ForegroundColor Cyan
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
foreach($video in $videos)
{
$video = $video.Replace("`"", "")
$inputFile = $videoPath + "\" + $video
# Pre-file-check if "HEVC" exist in filename skip filechecking:
if ($video -match "HEVC")
{
$codec = "HEVC"
} else {
# Get and check File info for codec, if it's already HEVC: - JSON query: (['media']['track'][1]['Format'])
$fileDetails = cmd /c $fileTester $inputFile --Output=JSON | ConvertFrom-Json
$codec = $fileDetails.media.track[1].Format
}
# If not already HEVC, convert video using NVEncC64:
if($codec -eq "HEVC")
{
# Configure new file naming:
if ($video -match "265")
{
$outputFile = $videoPath + "\" + $video.Replace("265", "264")
} else {
$outputFile = $videoPath + "\" + $video.Insert(($video.Length - 4), '-x264')
}
Write-Host "Analyzing video ($videoID of $count), needs conversion - number: $($convertedVideos + 1)" -ForegroundColor Magenta
Write-Host "$video `nto:`n$outputFile" -ForegroundColor White
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\$logFolderName\needs_conversion.log"
LogWrite "$outputFile"
} else
{
# Alert if video doesn't need conversion:
Write-Host "Analyzing video ($videoID of $count) - skip" -ForegroundColor Magenta
Write-Host "$video is already in correct format!" -ForegroundColor Green
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
# Increments not converted video counter:
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\$logFolderName\already_correct_codec.log"
LogWrite "$inputFile"
}
# Increments video counter:
$videoID = $videoID + 1
$outputFile = "" # Resets the variable to nothing, that in case of an error the old file is not affected.
}
#############################################################################################################
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

4
_extras/ffmpeg/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,68 @@
# Review converted videos:
#-----------------------------------------------------------------------
# Edit the $logFolderName variable to point to your desired logs folder:
#$logFolderName = "24-06-2020 - [run started at 17-31]"
$logFolderName = "2020-10-03 - [run started at 11-29]"
#-----------------------------------------------------------------------
$reviewPlayer = "C:\Program Files\VideoLAN\VLC\vlc.exe"
#############################################################################################################
# Evaluation part of the conversion script #
#############################################################################################################
if(!(Test-Path $reviewPlayer -PathType leaf))
{
Write-Host "vlc.exe not found, is the player installed? please check path in `"$reviewPlayer`"." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
$reviewFiles = Get-content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log" | Measure-Object Line
$ReviewCount = $reviewFiles.Lines
Write-Host "There are $ReviewCount files to review.." -ForegroundColor Yellow
Write-Host
$review_count = 1
foreach($review_pair in Get-Content -LiteralPath "$PSScriptRoot\..\logs\$logFolderName\review_needed.log")
{
$oldFile,$newFile = $review_pair.split(',')
Write-Host "Playing $review_count converted file: $newFile"
Write-Host
# Starting VLC Player with the file to review
Start-Process $reviewPlayer -ArgumentList `"$newFile`"
Start-Sleep -Seconds 6
# Wait until player is closed.
$processID = (Get-Process "vlc").id
Wait-Process -Id $processID
$msg = 'Do you want to keep the newly converted file and delete the old? (N for deleting NEW-file) [Y/N]'
do {
$response = Read-Host -Prompt $msg
} until (($response -eq 'n') -or ($response -eq 'y'))
if ($response -eq 'y')
{
Write-Host "delete old file, keep newly converted.."
$to_delete = $oldFile
} else
{
Write-Host "delete newly converted, keep old file"
$to_delete = $newFile
$failedVideos = $failedVideos + 1
$convertedVideos = $convertedVideos - 1
}
Write-Host "DELETED: $to_delete" -ForegroundColor Red
Remove-Item -LiteralPath $to_delete
Write-Host "------------------------------------------------------------------------------------------------"
Start-Sleep -Seconds 2
}
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

View File

@ -1,6 +0,0 @@
:min_age_days: 0
:directory: /mnt/movies
:log_location: ./log.txt
:preset: slower
:threads: 8
:max_new_file_size_ratio: 0.9

View File

@ -1,204 +0,0 @@
require 'rubygems'
require 'streamio-ffmpeg'
require 'fileutils'
require 'logger'
require 'yaml'
require 'peach'
VID_FORMATS = %w[.avi .flv .mkv .mov .mp4]
# Loads config file:
@config=YAML.load(File.read("./config.yml"))
# Logger setup stuff:
@logger=Logger.new(@config[:log_location])
@logger.level=Logger::INFO
@logger.info "\n New Run starting now......"
@logger.info "Config is being used: #{@config}"
# Check the file age:
def file_age(name)
(Time.now - File.ctime(name))/(24*3600)
end
def seconds_to_s(total_time)
total_time=total_time.to_int
return [total_time / 3600, total_time/ 60 % 60, total_time % 60].map { |t| t.to_s.rjust(2,'0') }.join(':')
end
# Creates a list (array) of all aged files:
def get_aged_files(directory)
out=[]
Dir.foreach(directory){|file|
next if file == '.' or file == '..' or file.start_with?('.')
fileName=File.join(directory,file)
if(File.file?(fileName)) then
if VID_FORMATS.include? File.extname(file) then
if(file_age(fileName)>=@config[:min_age_days]) then
out<<fileName
end
end
elsif File.directory?(fileName) then
out+=get_aged_files(fileName)
end
}
return out
end
# Returns a hash:
def get_candidate_files(possibileFiles)
out={
movies: [],
runtime: 0
}
times=[]
possibileFiles.peach(4) do |file|
if does_video_need_conversion?(file)
out[:movies]<<file
movie=FFMPEG::Movie.new(file)
times<<movie.duration
end
end
times.each{|time|
out[:runtime]=out[:runtime]+=time
}
out[:movies].shuffle!
return out
end
# Check if video needs conversion or not:
def does_video_need_conversion?(file)
movie=FFMPEG::Movie.new(file)
if movie.valid? then
if movie.video_codec!="hevc" then
unless File.exist?(get_temp_filename(file))
return true
end
end
end
return false
end
def get_base_name(file)
outFileName=File.join(
File.dirname(file),
"#{File.basename(file,'.*')}")
end
def get_temp_filename(file)
"#{File.join(
File.dirname(file),
".#{File.basename(file,'.*')}")}.tmp.mp4"
end
def safe_convert_file(original_video,filename)
begin
return convert_file(original_video,filename)
rescue StandardError => e
@logger.error "Problem processing a video"
@logger.error e
end
return nil
end
# Main function for conversion:
def convert_file(original_video,filename)
options={
video_codec: 'libx265',
threads: @config[:threads],
custom: "-preset #{@config[:preset]} -crf 25 -c:a copy".split
}
outFileName = get_base_name(filename)
error_thrown=nil
begin
startTime=Time.now
out = original_video.transcode(get_temp_filename(filename),options){ |progress|
duration=Time.now-startTime
remaining=(duration/progress)*(1-progress)
if(remaining>99999999) then
print "Progress converting #{filename.split('/').last} : #{(progress*100).round(1)}% \r"
else
print "Progress converting #{filename.split('/').last} : #{(progress*100).round(1)}% ETA is #{seconds_to_s(remaining)} \r"
end
}
rescue StandardError => e
error_thrown=e
puts e.to_s
end
puts "Done with #{filename.split('\\').last}"
if ( error_thrown )
@logger.error "A video file failed to transcode correctly"
@logger.error error_thrown
FileUtils.rm(get_temp_filename(filename)) if File.exists?(get_temp_filename(filename))
FileUtils.touch(get_temp_filename(filename))
File.write(get_temp_filename(filename),
[
'An exception occured while transocding this movie.',
error_thrown
].join('\n'))
elsif (out.size>original_video.size*@config[:max_new_file_size_ratio])
@logger.warn "A video file, after transcoding was not at least #{@config[:max_new_file_size_ratio]} the size of the origional (new: #{out.size} old: #{original_video.size}). Keeping origonal #{filename}"
FileUtils.rm(get_temp_filename(filename))
FileUtils.touch(get_temp_filename(filename))
File.write(get_temp_filename(filename), "transcoded video not enough smaller than the origional.")
return nil
else
FileUtils.mv(get_temp_filename(filename),"#{outFileName}.mp4")
if filename!="#{outFileName}.mp4" then
FileUtils.rm(filename)
end
return out
end
end
def status(app)
possible_files=get_aged_files(@config[:directory])
puts "There are a total of #{possible_files.size} files that may need to be converted."
candidate_files= get_candidate_files(possible_files)
puts "There are a total of #{candidate_files[:movies].size} files that have not been converted yet."
puts "Total Duration: #{seconds_to_s(candidate_files[:runtime])}"
end
@total_processing_time=0
@processed_video_duration=0
def iterate
possible_files=get_aged_files(@config[:directory])
@logger.info "There are a total of #{possible_files.size} files that may need to be converted."
@logger.debug "Files to be checked: #{possible_files}"
candidate_files= get_candidate_files(possible_files)
@logger.info "There are a total of #{candidate_files[:movies].size} files that have not been converted yet."
@logger.debug "Candidate Files that need to be re-encoded: #{candidate_files}"
@logger.info "Total Duration: #{seconds_to_s(candidate_files[:runtime])}"
remaining_runtime=candidate_files[:runtime]
candidate_files[:movies].each_with_index do |file,index|
@logger.info "Starting to transcode file #{index+1} of #{candidate_files[:movies].size}: #{file}"
unless does_video_need_conversion?(file)
@logger.info "Video already converted, scanning again"
return true
end
startTime=Time.now
video=FFMPEG::Movie.new(file)
converted_video=safe_convert_file(video,file)
duration=Time.now - startTime
remaining_runtime-=video.duration
if !converted_video.nil? then
@total_processing_time+=duration
@processed_video_duration+=video.duration
end
avg=@processed_video_duration/@total_processing_time
@logger.info "Average videotime/walltime: #{avg} Estimated time remaining #{seconds_to_s(remaining_runtime/avg)}"
end
return false
end
while iterate do end

290
convert_Videos.ps1 Normal file
View File

@ -0,0 +1,290 @@
# Converts videos to HEVC for given path:
# https://github.com/rigaya/NVEnc/releases
#-----------------------------------------------------------------------
# Edit the $videoPath variable to point to your video-files folder:
#$videoPath = 'M:\1_movies\_main.movies'
$videoPath = 'M:\2_serien\_main.series'
#$videoPath = 'M:\3_dokus'
#-----------------------------------------------------------------------
# HEVC profiles: main, main10, main444
$hevc_profile = 'main10'
$forceReviewAll = $false # If set to $true all failed conversions are reviewed even those that are obviously corrupt.
$NVEncoder = "$PSScriptRoot\encoder\NVEncC64.exe"
$fileTester = "$PSScriptRoot\mediainfo.exe"
$reviewPlayer = "C:\Program Files\VideoLAN\VLC\vlc.exe"
#############################################################################################################
# Testing and conversion preparations #
#############################################################################################################
if (!(Test-Path $NVEncoder -PathType leaf)) {
Write-Host "NVEncC64.exe not found, please check path in `"$NVEncoder`"." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
# Get video list from provided path:
$videos = Get-ChildItem -LiteralPath $videoPath -Name -Recurse -Include ('*.mp4', '*.mkv') #('*.avi', '*.mp4', '*.mkv') - avi currently doesn't work.
# Instantiating used variables:
$videoID = 1
$convertedVideos = 0
$notConvertedVideos = 0
$failedVideos = 0
# Square brackets are used as wildcards in Powershell.
# To recognize this as normal text, the LogWite function uses the -LiteralPath parameter instead of the -Path parameter.
$logFolderName = $((Get-Date).tostring("yyyy-MM-dd") + ' - [run started at ' + (Get-Date).tostring("HH-mm") + ']')
$count = $videos.Count
if ($videos.Count -lt 1) {
Write-Host "No videos found in: $videoPath" -ForegroundColor Red
Read-Host -Prompt "Press Enter to exit"
exit
}
# Log write function:
Function LogWrite {
Param ([string]$logstring)
Add-content -LiteralPath $Logfile -value $logstring
}
# Create new log directory:
New-Item -itemType Directory -Path $PSScriptRoot\logs\ -Name $logFolderName > $null
# Calculates the total size of the given directory in GB:
Write-Host "Inizialization in progress.."
$folderSizeInGB = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
Clear-Host # Clears the upper message from the window
#############################################################################################################
# END - Testing and conversion preparations
#############################################################################################################
# Main conversion process #
#############################################################################################################
Write-Host "Found $count videos with a total size of $folderSizeInGB - starting converter.." -ForegroundColor Cyan
Write-Host
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
foreach ($video in $videos) {
$video = $video.Replace("`"", "")
$inputFile = $videoPath + "\" + $video
# Pre-file-check if "HEVC" exist in filename skip filechecking:
if ($video -match "HEVC") {
$codec = "HEVC"
}
else {
# Get and check File info for codec, if it's already HEVC: - JSON query: (['media']['track'][1]['Format'])
$fileDetails = cmd /c $fileTester $inputFile --Output=JSON | ConvertFrom-Json
$codec = $fileDetails.media.track[1].Format
}
# If not already HEVC, convert video using NVEncC64:
if ($codec -ne "HEVC") {
# Configure new file naming:
if ($video -match "264") {
$outputFile = $videoPath + "\" + $video.Replace("264", "265")
}
else {
$outputFile = $videoPath + "\" + $video.Insert(($video.Length - 4), '-HEVC')
}
Write-Host "Analyzing video ($videoID of $count), starting convertion of number: $($convertedVideos + 1) please wait.." -ForegroundColor Magenta
Write-Host "$video `nto:`n$outputFile" -ForegroundColor White
Write-Host
# If the Subtitles should not be copied, delete the "--sub-copy 1,2" argument.
$arguments = "--input `"$inputFile`" --codec hevc --audio-copy 1,2,3,4 --sub-copy 1,2,3,4 --profile $hevc_profile --output `"$outputFile`""
Start-Process $NVEncoder -ArgumentList $arguments -WindowStyle Minimized
$processName = 'NVEncC64'
Start-Sleep -Seconds 1
# Sets to use 3 cores (always set to one less core that your CPU has)
# 2 Cores = 3, 3 Cores = 7, 4 cores = 15, 5 cores = 31, 6 cores = 63
# Code to calculate for your CPU:
# $noOfCores = Get-WmiObject Win32_Processor | Measure-Object NumberOfLogicalProcessors -Sum
# $noOfCores.Sum = $noOfCores.Sum - 1
# [math]::Pow(2,$($noOfCores).Sum) - 1
#
$process = Get-Process $processName; $process.ProcessorAffinity = 7
Start-Sleep -Seconds 2
# Sets priorty to High
# Values: High, AboveNormal, Normal, BelowNormal, Low
$process = Get-Process -Id $process.Id
$process.PriorityClass = 'High'
# Waits for process to complete
$processID = (Get-Process $processName).id
Wait-Process -Id $processID
if (Test-Path -LiteralPath $outputFile)
{ # Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed.
#No characters are interpreted as wildcard characters.
Write-Host "CONVERSION DONE! FILE: `"$outputFile`" FOUND" -ForegroundColor Yellow
Start-Sleep -Seconds 4
# Check if File is valid; the duration of video must be the same! (['media']['track'][0]['Duration']):
$fileDetails_new = cmd /c $fileTester $outputFile --Output=JSON | ConvertFrom-Json
# This code gets the duration and splits it in two parts, only the part before the "." is needed:
$StreamSize_old, $notused = $($fileDetails.media.track[0].Duration).split('.')
$StreamSize_new, $notused = $($fileDetails_new.media.track[0].Duration).split('.')
Write-Host
Write-Host "Old Duration was: $StreamSize_old `nnew Duration is: $StreamSize_new" -ForegroundColor White
Write-Host
if ($StreamSize_new -eq $StreamSize_old) {
# Delete old video File!
Write-Host "Conversion Successful! - Deleting old file.."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
}
else {
if (($StreamSize_new -eq $($StreamSize_old - 1)) -or ($($StreamSize_new - 1) -eq $StreamSize_old)) {
# Newly converted file does not match exactly the old duration. But diffrence its not more than 1 sec!
Write-Host "Conversion Done! - Streamsize is not exactly the same. But still OK - Ignoring!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $inputFile
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\successfully_converted.log"
LogWrite "$outputFile"
}
elseif (($StreamSize_new -ge $($StreamSize_old - 20)) -or ($($StreamSize_new - 20) -ge $StreamSize_old)) {
# New converted file has been converted but with up to max 20 seconds deviation!
Write-Host "Conversion Done! - But streamsize is not the same. - Please review!"
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
}
else {
if ($forceReviewAll) {
# Mark probably corrupted file for review
Write-Host "Conversion Failed! - Marked for review! because it is desired."
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
$review = $true
$convertedVideos = $convertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\review_needed.log"
LogWrite "$inputFile,$outputFile"
}
else {
# Delete obviously corrupt File!
Write-Host "Conversion Failded! - Deleting new converted file.." -ForegroundColor Red
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
Remove-Item -LiteralPath $outputFile
$failedVideos = $failedVideos + 1
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\error_during_conversion.log"
LogWrite "$inputFile,$StreamSize_new,$StreamSize_old,"
}
}
}
}
}
else {
# Alert if video doesn't need conversion:
Write-Host "Analyzing video ($videoID of $count) - skip" -ForegroundColor Magenta
Write-Host "$video is already in correct format!" -ForegroundColor Green
Write-Host
Write-Host "------------------------------------------------------------------------------------------------"
# Increments not converted video counter:
$notConvertedVideos = $notConvertedVideos + 1
$Logfile = "$PSScriptRoot\logs\$logFolderName\skipped_files.log"
LogWrite "$inputFile"
}
# Increments video counter:
$videoID = $videoID + 1
$outputFile = "" # Resets the variable to nothing, that in case of an error the old file is not affected.
}
#############################################################################################################
# END - Main conversion process
#############################################################################################################
# Evaluation part of the conversion script #
#############################################################################################################
if ($review) {
$reviewFiles = Get-content -LiteralPath "$PSScriptRoot\logs\$logFolderName\review_needed.log" | Measure-Object Line
$ReviewCount = $reviewFiles.Lines
Write-Host "There are $ReviewCount files to review.." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to start reviewing first file"
Write-Host
Write-Host "################################################################################################"
$review_count = 1
foreach ($review_pair in Get-Content -LiteralPath "$PSScriptRoot\logs\$logFolderName\review_needed.log") {
$oldFile, $newFile = $review_pair.split(',')
Write-Host "Playing $review_count converted file: $newFile"
Write-Host
# Starting VLC Player with the file to review
Start-Process $reviewPlayer -ArgumentList `"$newFile`"
Start-Sleep -Seconds 6
# Wait until player is closed.
$processID = (Get-Process "vlc").id
Wait-Process -Id $processID
$msg = 'Do you want to keep the newly converted file and delete the old? (N for deleting NEW-file) [Y/N]'
do {
$response = Read-Host -Prompt $msg
} until (($response -eq 'n') -or ($response -eq 'y'))
if ($response -eq 'y') {
Write-Host "delete old file, keep newly converted.."
$to_delete = $oldFile
}
else {
Write-Host "delete newly converted, keep old file"
$to_delete = $newFile
$failedVideos = $failedVideos + 1
$convertedVideos = $convertedVideos - 1
}
Write-Host "DELETED: $to_delete" -ForegroundColor Red
Remove-Item -LiteralPath $to_delete
Write-Host "------------------------------------------------------------------------------------------------"
Start-Sleep -Seconds 2
}
}
Write-Host "Final calculations are in progress.."
$folderSizeInGB2 = "{0:N2} GB" -f ((Get-ChildItem $videoPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1GB)
if ($notConvertedVideos -eq 0) {
Write-Host "Finished converted $convertedVideos out of $count videos." -ForegroundColor Green
}
else {
if ($failedVideos -eq 0) {
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because of already correct codec!" -ForegroundColor Green
}
else {
Write-Host "Finished converted $convertedVideos out of $count videos. - $notConvertedVideos where not converted because error or already correct codec!!" -ForegroundColor Green
Write-Host "$failedVideos have failed!" -ForegroundColor Red
}
}
Write-Host "Directory size before conversion: $folderSizeInGB"
Write-Host "Directory size after conversion: $folderSizeInGB2"
Read-Host -Prompt "Press Enter to exit"
#############################################################################################################
# END - conversion script

BIN
encoder/NVEncC64.exe Normal file

Binary file not shown.

BIN
encoder/avcodec-59.dll Normal file

Binary file not shown.

BIN
encoder/avfilter-8.dll Normal file

Binary file not shown.

BIN
encoder/avformat-59.dll Normal file

Binary file not shown.

BIN
encoder/avutil-57.dll Normal file

Binary file not shown.

BIN
encoder/hdr10plus_gen.exe Normal file

Binary file not shown.

BIN
encoder/libass-9.dll Normal file

Binary file not shown.

BIN
encoder/libvmaf.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
encoder/nvrtc64_101_0.dll Normal file

Binary file not shown.

BIN
encoder/swresample-4.dll Normal file

Binary file not shown.

4
logs/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

BIN
mediainfo.exe Normal file

Binary file not shown.