Skip to content

Instantly share code, notes, and snippets.

@tostka
Last active March 28, 2018 00:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tostka/40e75779978c308883ec89aedf9fb485 to your computer and use it in GitHub Desktop.
Save tostka/40e75779978c308883ec89aedf9fb485 to your computer and use it in GitHub Desktop.
convert-ToMp3.ps1 - Movie to Mp3 conversion wrapper script for ffmpeg or VLC player.
# convert-ToMp3.ps1
<# This gist is TRIMMED to ust the core pieces #>
#*================v FUNCTIONS v================
#*----------------v Function convert-ToMp3 v------
function convert-ToMp3 {
<# TRIMMED #>
[CmdletBinding()]
PARAM (
[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0,Mandatory=$True,HelpMessage="File(s) to be transcoded")]
$InputObject,
[parameter(HelpMessage="Bitrate for transcoded output (defaults to 320k)")]
[int]$bitrate=320,
[parameter(HelpMessage="Samplerate for transcoded output (defaults to 44100)")]
[int]$samplerate=44100,
[Parameter(Mandatory=$false,HelpMessage="Specify Site to analyze [-Encoder (FFMPEG|VLC]")]
[ValidateSet("FFMPEG","VLC")]
[string]$encoder="VLC",
[Parameter(HelpMessage='ShowProgress [$switch]')]
[switch] $showProgress,
[Parameter(HelpMessage='Debugging Flag [$switch]')]
[switch] $showDebug,
[Parameter(HelpMessage='Whatif Flag [$switch]')]
[switch] $whatIf
) ; # PARAM-E
BEGIN {
$rgxInputExts="(?i:^\.(MPEG|MP3|AVI|ASF|WMV|WMA|MP4|MOV|3GP|OGG|OGM|MKV|WEBM|WAV|DTS|AAC|AC3|A52|FLAC|FLV|MXF|MIDI|SMF)$)" ;
$outputExtension = ".mp3" ;
$audio_codec="mp3" ;
#"mpga"
$channels = 2 ;
$mux="dummy" ; # for mp3 audio-only extracts, use the dummy mux
#"mpeg1"
$progInterval= 500 ; # write-progress interval in ms
$iProcd= 0 ;
$continue = $true ;
$programFiles = ${env:ProgramFiles(x86)};
if($programFiles -eq $null) { $programFiles = $env:ProgramFiles; } ;
switch($encoder){
"VLC" {$processName = $programFiles + "\VideoLAN\VLC\vlc.exe" ; }
"FFMPEG" {$processName = "C:\apps\ffmpeg\bin\ffmpeg.exe" }
} ;
if(!(test-path -path $processName)){throw "MISSING/INVALID $($encoder) install path!:$($processName)"} ;
write-verbose -verbose:$true "$((get-date).ToString("HH:mm:ss")):=== v PROCESSING STARTED v ===" ;
$progParam=@{
CurrentOperation ="Beginning";
Status="Preparing Processing...";
PercentComplete = 0;
} ;
} # BEG-E ;
PROCESS {
$ttl = ($InputObject | measure).count ;
$iProcd=0 ; $swp = [System.Diagnostics.Stopwatch]::StartNew() ;
# foreach, to accommodate arrays passed in
foreach($inputFile in $InputObject) {
$continue = $true ;
try {
if($inputfile.GetType().fullname -ne "System.IO.DirectoryInfo"){
switch ($inputfile.GetType().fullname){
"System.IO.FileInfo" {
if($tf=Get-childitem -path $inputFile.fullname -ea Stop) {
} else {
write-host -ForegroundColor red "Unable to read infile: $($inputFile.fullname)" ;
throw "MISSING/INVALID inputfile:$($inputFile.fullname)"
} ;
} ;
"System.String" {
# if it's a string, it's not going to have a fullname prop - it's a full path string
if($tf=Get-childitem -path $inputFile) {
} else {
write-host -ForegroundColor red "Unable to read infile: $($inputFile.fullname)" ;
throw "MISSING/INVALID inputfile:$($inputFile.fullname)"
} ;
};
default {
write-host -ForegroundColor red "Unable to read infile: $($inputFile.fullname)" ;
"inputfile.GetType().fullname:$($inputfile.GetType().fullname)" ;
throw "UNRECOGNIZED TYPE OBJECT inputfile:$($inputFile.fullname). ABORTING!" ;
} ;
} ;
if($tf.extension -notmatch $rgxInputExts ) {throw "UNSUPPORTED INPUTFILE TYPE:$($inpuptFile)"} ;
$outputFileName=(join-path -path $tf.Directory -ChildPath "$($tf.BaseName)$($outputExtension)") ;
# Generate a unique filename with a specific extension (non-tmp, leverages the GUID-generating call):
$tempout=(join-path -path $tf.Directory -ChildPath "$([guid]::NewGuid().tostring())$($outputExtension)").replace("\","\\") ;
$inputFileName=$tf.FullName.replace("\","\\") ;
if($showDebug) {write-verbose -verbose:$true "`$outputFileName:$outputFileName`n`$tempout:$($tempout)" } ;
switch($encoder){
"VLC" {
# build args where we can see whats going on
# 1st spec dummy/non-GUI pass, and input filename $($inputFileName)
$processArgs = "-I dummy -v `"$($inputFileName)`"" ;
if($whatif){
write-verbose -verbose:$true "-whatif detected, test-transcoding only the first 30secs" ;
$processArgs+=" --stop-time=30" ;
} ;
# build output transcode settings
$processArgs+= " :sout=#transcode{" ;
$processArgs+= "vcodec=none,acodec=$($audio_codec),ab=$($bitrate),channels=$($channels),samplerate=$($samplerate)" ;
$processArgs+= "acodec=$($audio_codec),ab=$($bitrate),channels=$($channels),samplerate=$($samplerate)" ;
# end output transcode settings
$processArgs+="}" ;
# add the output file specs & mux
$processArgs+=":standard{access=`"file`",mux=$($mux),dst=`"$($tempout)`"}"
# tell it to exit on completion
$processArgs+=" vlc://quit" ;
}
"FFMPEG" {
$processArgs = "-i `"$($inputFileName)`"" ;
# $bitrate=320
$processArgs+= " -ab $($bitrate * 1000)" ;
# $samplerate=44100
$processArgs+= " -ar $($samplerate)" ;
$processArgs+= " -f mp3" ;
$processArgs+= " `"$($tempout)`"" ;
}
} ;
if($showDebug){
write-verbose -verbose:$true "`$processName:$($processName | out-string)" ;
write-verbose -verbose:$true "`$processArgs:$($processArgs | out-string)" ;
} ;
# launch command
# capture and echo back errors from robocopy.exe
$soutf= [System.IO.Path]::GetTempFileName() ;
$serrf= [System.IO.Path]::GetTempFileName() ;
"Cmd:$($processName) $($processArgs)" ;
$process = Start-Process -FilePath $processName -ArgumentList $processArgs -NoNewWindow -PassThru -Wait -RedirectStandardOutput $soutf -RedirectStandardError $serrf ;
switch($process.ExitCode){
0 {
"ExitCode 0 returned (No Errors, File converted)"
# ren $tempout => $outputFileName
if($rf=get-childitem -path $tempout ){
# renames fail if there's an existing fn clash -> ren the clash, -ea 0 to suppress notfound errors
if($cf=get-childitem -path ($outputFileName | split-path -Leaf) -ea 0 ){
Rename-Item -path $rf.fullname -NewName "$($cf.BaseName)-$((get-date).tostring('yyyyMMdd-HHmmtt'))$($cf.Extension)" ;
} else {
Rename-Item -path $rf.fullname -NewName $($outputFileName | split-path -Leaf)
};
} else {
throw "No matching temporary output file found: $($tmpout)" ;
} ;
} ;
1 {"ExitCode 1 returned (fatal error)"} ;
default {write-host "ERROR during VLC Transcoding: Non-0/1 ExitCode returned $($process.ExitCode)" ; write-host "`a" ;} ;
} ;
if((get-childitem $soutf).length){
if($ShowDebug){(gc $soutf) | out-string ;} ;
remove-item $soutf ;
} ;
if((get-childitem $serrf).length){
if($ShowDebug){(gc $serrf) | out-string ;} ;
remove-item $serrf ;
} ;
$iProcd++ ;
[int]$pct = ($iProcd/$ttl)*100 ;
} else {
# code leak to throw out directories
# System.IO.DirectoryInfo
"(Skipping $($inputfile) -- Directory)" ;
} ;
} catch {
# BOILERPLATE ERROR-TRAP
<# TRIMMED #>
write-output "$((get-date).ToString("HH:mm:ss") ): Error Details: $($_)" ;
Continue ;
# Exit; here if you want processing to die and not continue on next for-pass
} # try/cat-E ;
} # # loop-E ;
} # PROC-E ;
END {
write-verbose -verbose:$true "$((get-date).ToString("HH:mm:ss")):$($iProcd) conversions processed" ;
write-verbose -verbose:$true "$((get-date).ToString("HH:mm:ss")):=== ^ PROCESSING COMPLETE ^ ===" ;
} # END-E
} #*----------------^ END Function convert-ToMp3 ^--------
#*================^ END FUNCTIONS ^================
#*================v SUB MAIN v================
<# TRIMMED #>
$error.clear() ;
TRY {
$useTempDir=$false ;
if ($InputObjec -is [system.array]){
$oPath = (split-path -Path $InputObject.fullname -Parent)[0] ;
} else {
$opath = (split-path -Path $InputObject.fullname -Parent) ;
}
if( $oPath -match "(\,|')"){
# also need to detect ''s in the path
write-host "$((get-date).ToString('HH:mm:ss')):Banned Char detected in input path (causes VLC path-misparses)`nusing temp directory for all processing.";
#exit;
$useTempDir=$true ;
}
[array]$opfiles =@() ; [array]$renfiles =@() ;
foreach ($i in $InputObject){
if ($i.mode.substring(0,1) -ne "d") {
if($i.name -match "(\[|\])"){
$name = $i.name.replace("[","_").replace("]","_") ;
$npath=(join-path -path $i.Directory -childpath "$($name)") ;
$spath=$i.fullname ;
write-host -foregroundcolor green "Square-brackets in file name: Renaming to $($name): " ;
if($showDebug){"`$spath:$($spath)`nto `$npath:$($npath)`n"; } ;
[System.IO.File]::Move($spath, $npath ) ;
$renfiles+=$spath ;
$opfiles+=$npath ;
} else {
$opfiles+= $i.fullname ;
} ;
} else {
write-host -foregroundcolor green "Skipping dir object: $($i.name)" ;
} ;
} # loop-E
if($host.version.major -ge 3){
$fSplat=[ordered]@{
Dummy = $null ;
} ;
} else {
$fSplat = New-Object Collections.Specialized.OrderedDictionary ;
} ;
If($fSplat.Contains("Dummy")){$fSplat.remove("Dummy")} ;
# Populate the $fSplat with fields, post creation
if($useTempDir){
# capture pwd
$sdir=get-location ;
push-location ;
$TempDir = [System.Guid]::NewGuid().ToString()
# create temporary folder (this creates it as a sub of the pwd, unless you pre set-location)
set-location ([System.IO.Path]::GetTempPath()) ;
$bout=New-Item -Type Directory -Name $TempDir ; # create the tmpdir in the profle temp path (C:\Users\MyAccount\AppData\Local\Temp\)
Set-Location $TempDir
write-host "Processing conversion in temp dir:$((get-location).path)" ;
copy-item $opfiles -Destination (get-location) ;
get-childitem | %{
if($_){
$fSplat.Add("InputObject",$($_)) ;
} else {throw "INVALID/MISSING -InputObject!. ABORTING!" };
} ;
} else {
# if squarebracket ren'd, we can't use inputobj
if($renfiles){
$fSplat.Add("InputObject",$($opfiles))
} elseif($InputObject){$fSplat.Add("InputObject",$($InputObject))}
else {throw "INVALID/MISSING -InputObject!. ABORTING!" };
}
if($bitrate){$fSplat.Add("bitrate",$($bitrate))} ;
if($samplerate){$fSplat.Add("samplerate",$($samplerate))} ;
if($showDebug){$fSplat.Add("showDebug",$($showDebug))} ;
if($whatif){$fSplat.Add("whatif",$($whatif))} ;
if($showProgress){$fSplat.Add("showProgress",$($showProgress))} ;
# 8:21 PM 5/3/2017
if($encoder){$fSplat.Add("encoder",$($encoder))} ;
} CATCH {
Write-Error "$(get-date -format "HH:mm:ss"): Failed processing $($_.Exception.ItemName). `nError Message: $($_.Exception.Message)`nError Details: $($_)" ;
if($sDir){set-location $sDir} ; # return to $sdir
Exit #Opts: STOP(debug)|EXIT(close)|Continue(move on in loop cycle)
} # try/catch-E ;
if($showDebug){write-host -foregroundcolor green "Conv Params`$fsplat:$($fsplat | out-string)" ; }
$bRet=convert-ToMp3 @fSplat ;
Try {
# if we cached, need to move the mp3 back to original dir, and remove all else
if($useTempDir){
write-host -foregroundcolor green "$((get-date).ToString('HH:mm:ss')):Cleaning up temp cache directory..." ;
Move-Item -path .\*.mp3 -Destination $oPath ;
Remove-Item *.* -Force ; # Remove everything we put in our temporary folder
Set-Location .. # go one level up
Remove-Item $TempDir # And delete our temporary folder
set-location $sDir ; # return to $sdir
} ;
# if renamed need to restore the name
if($fRename -eq $true){
rename-item -path (join-path -path (get-location) -childpath $ftempname) -newname $origname ;
}
} CATCH {
Write-Error "$(get-date -format "HH:mm:ss"): Failed processing $($_.Exception.ItemName). `nError Message: $($_.Exception.Message)`nError Details: $($_)" ;
if($sDir){set-location $sDir} ; # return to $sdir
Exit #Opts: STOP(debug)|EXIT(close)|Continue(move on in loop cycle)
} # try/catch-E ;
<# TRIMMED #>
#*================^ END SUB MAIN ^================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment