20160503

PlayOn Stream Recorder Ad Removal

I have been enjoying using PlayOn to record video streams for my Plex server to host, then syncing them to my mobile devices to watch on the bus or when I have some down time, without using my mobile data.

I was a little annoyed with the fact that I needed to use the PlayOn player to skip the ads, as the PlayOn app doesn't let you store the files locally.  To fix this I created a Powershell script that uses ffmpeg to remove the chapters in the mp4 video files that are marked as "Advertisement".  I also trim the first few and last few seconds of the video to remove the annoying PlayOn logo.

It is not perfect, especially for HBO, they have been adding lots of previews/trailers for their content, and PlayOn doesn't recognize it as an ad, but it does a pretty good job on most.

Here is the script, it's a little sloppy but is good enough.
#
# Script.ps1
#
$InputBasePath = "E:\Video\PlayOnRecordings\"
$OutputBasePath = "S:\Video\PlayOnRecordings\"
$vidsToConvert = Get-ChildItem "$InputBasePath*.mp4" -Recurse
#$vidsToConvert = Get-ChildItem "E:\Video\PlayOnRecordings\Xfinity\*.mp4"
foreach ($vid in $vidsToConvert) {
$TARGETFILEPATH = $vid.FullName.Replace($InputBasePath,$OutputBasePath).Replace("PlayOnRecordings","TV Shows")
$TARGETDIR = $TARGETFILEPATH.Replace($vid.Name,"")

if(!(Test-Path -Path $TARGETFILEPATH)){
Write-Output $vid.FullName
New-Item $env:TEMP\MP4ChapterInfo.txt -type file -force
ffprobe -loglevel panic $vid -show_chapters -sexagesimal -print_format csv 1> $env:TEMP\MP4ChapterInfo.txt
$csvVidChapters = Import-Csv $env:TEMP\MP4ChapterInfo.txt -Header @("ObjName","Index","Fraction","StartMiliseconds","StartTime","EndMiliseconds","EndTime","ChapterTitle")

$firstVideo = 1
$lineCount = 1
Write-Output "Chapter Lines: "$csvVidChapters.length
foreach ($line in $csvVidChapters){
$OutputFile = ""
If ([convert]::ToInt32($line.Index) -lt 10) {
$OutputFile = ($env:TEMP + "\temp_" + $line.ChapterTitle + "_0" + $line.Index + $vid.Extension)

}
Else{
$OutputFile = ($env:TEMP + "\temp_" + $line.ChapterTitle + "_" + $line.Index + $vid.Extension)
}

if($firstVideo -eq 1){
Write-Output "Split FIRST Chapter: "$OutputFile
#$newStartTime = ([TimeSpan]::Parse($line.StartTime)).TotalSeconds
#$newStartTime = $newStartTime + 5
#$tsStart =  [timespan]::fromseconds($newStartTime)
#Write-Output "First File, Trim Playon Tag. "("{0:hh\:mm\:ss\,fff}" -f $tsStart)
#$newStartTime = ("{0:hh\:mm\:ss\,fff}" -f $tsStart) -replace ",","."
ffmpeg -loglevel panic -i $vid -ss $line.StartTime -to $line.EndTime -async 1 -acodec copy -vcodec copy $OutputFile
$firstVideo = 0
}
else{
Write-Output "Split Chapter: "$OutputFile
if($lineCount -eq $csvVidChapters.length){
$newEndTime = ([TimeSpan]::Parse($line.EndTime)).TotalSeconds
$newEndTime = $newEndTime - 10
$tsEnd =  [timespan]::fromseconds($newEndTime)
Write-Output "Last File, Trim Playon Tag. "("{0:hh\:mm\:ss\,fff}" -f $tsEnd)
$newEndTime = ("{0:hh\:mm\:ss\,fff}" -f $tsEnd) -replace ",","."
ffmpeg -loglevel panic -i $vid -ss $line.StartTime -to $newEndTime -async 1 -acodec copy -vcodec copy $OutputFile
}
else{
ffmpeg -loglevel panic -i $vid -ss $line.StartTime -to $line.EndTime -async 1 -acodec copy -vcodec copy $OutputFile
}

}
$lineCount++
}
Remove-Item $env:TEMP\MP4ChapterInfo.txt

$vidsToCombine = Get-ChildItem $env:TEMP\temp_Video*.mp4

$count = 0
New-Item $env:TEMP\MP4ConcatList.txt -type file -force
foreach ($vid_Video in $vidsToCombine) {
Write-Output $vid_Video.Length

$VidLength = ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -sexagesimal $vid_Video
Write-Output "Section Length: "([TimeSpan]::Parse($VidLength)).TotalSeconds

#If ($vid_Video.Length -gt 2000000){
If (([TimeSpan]::Parse($VidLength)).TotalSeconds -gt 30){
Write-Output "Add Concat: "$vid_Video.FullName
"file '" + $vid_Video.FullName + "'" | Out-File $env:TEMP\MP4ConcatList.txt -Append -encoding default
$count++
}
else{
Write-Output "Skip Video Concat: "$vid_Video.FullName
}
Write-Output $count
}


if(!(Test-Path -Path $TARGETDIR )){
Write-Output "Directory does not exist - CREATE DIRECTORY: "$TARGETDIR
New-Item -ItemType directory -Path $TARGETDIR
}

!(Test-Path -Path $TARGETFILEPATH)
$TARGETFILEPATH
If(!(Test-Path -Path $TARGETFILEPATH)){
Write-Output "Count: "$count
If ($count -gt 0){
Write-Output "Build Vid from Chapters..."
ffmpeg -loglevel panic -f concat -i $env:TEMP\MP4ConcatList.txt -c copy $TARGETFILEPATH
}
else{
Write-Output "No Chapters - Copy Vid to: "$TARGETFILEPATH

$newStartTime = 0 #([TimeSpan]::Parse("0:00:00:00,000")).TotalSeconds
$newStartTime = $newStartTime + 5
$tsStart =  [timespan]::fromseconds($newStartTime)
Write-Output "Trim Playon Tag. "("{0:hh\:mm\:ss\,fff}" -f $tsStart)
$newStartTime = ("{0:hh\:mm\:ss\,fff}" -f $tsStart) -replace ",","."

$VidLength = ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -sexagesimal $vid
Write-Output $VidLength

$newEndTime = ([TimeSpan]::Parse($VidLength)).TotalSeconds
$newEndTime = $newEndTime - 10
$tsEnd =  [timespan]::fromseconds($newEndTime)
Write-Output "Last File, Trim Playon Tag. "("{0:hh\:mm\:ss\,fff}" -f $tsEnd)
$newEndTime = ("{0:hh\:mm\:ss\,fff}" -f $tsEnd) -replace ",","."

ffmpeg -loglevel panic -i $vid -ss $newStartTime -to $newEndTime -async 1 -acodec copy -vcodec copy $TARGETFILEPATH
#Copy-Item $vid -Destination $TARGETFILEPATH
}
}

###Remove-Item $strRemoval -include $vid.Extension
Remove-Item $env:TEMP\MP4ConcatList.txt
Remove-Item $env:TEMP\temp_Video_*.mp4
Remove-Item $env:TEMP\temp_Advertisement_*.mp4

}
else{
Write-Output "Vid Exists Skip: "$vid.FullName
}
}