録画してできたM2TSは1時間番組でもファイルサイズが7GBを超えるため保存やコピーに手間がかかります。そこで、MP4へ変換してファイルサイズを落として扱いやすくする必要があります。
スポーツ番組や旅番組など画像の変化の激しいものや古い映画などのようにノイズの多いものはファイルサイズは半分程度までしか落ちないこともありますが、アニメなどの変化の少ないものは10分の1程度まで落とせます。概ね2-3割程度まで落とせると考えればいいでしょう。
また、変換の際にCMを抜くとさらにファイルサイズを落とせます。通常2割弱程度がCMですので、圧縮後のファイルからさらに2割サイズを落とすことができます。ただし、手動でCMカットはかなりの手間ですので、WindowsアプリのAmatsukazeを利用します。
Amatsukazeはコマンドラインからも実行できるのでバッチファイルによる自動処理が可能です。ただし、バッチファイルは制限が多く複雑な自動処理には向きません。また、変換だけでなく、変換したファイルをNASへコピーしたり、途中で停止したり処理の確認をしたりと、様々な追加要望が出てきます。
そこで、バッチファイルからPowershellを起動する形で処理を自動化します。Powershellでは起動オプションによって動作を変えるように実装し、バッチファイル側で機能を選択する形で様々なパターンに対応できるようにしました。PowershellはWindowsのデフォルトシェルなので、Pythonのように別途ソフトをインストールすることなく自動化処理が可能になります。
Amatsukazeを利用してmp4への変換を行うPowershellスクリプトとスクリプト起動用のバッチファイルを以下に示します。具体的には以下の処理を順番に実行します。
- M2TSファイルをWindowsのローカルストレージにコピーする
- Amatsukazeを使ってCM抜き処理とmp4変換
- mp4とass(字幕ファイル)のNASへのバックアップ
- リモート視聴用ファイルの作成と再生用サーバーへのコピー
- PCシャットダウン
ファイルの保存位置など環境に依存するパラメータについては、スクリプトの先頭にまとめていますので、こちらを編集してください。
Amatsukazeはアプリ単体で動作するように事前に設定してください。変換パラメータについてはスクリプト側で指定するので、特に調整する必要はありません。
なお、このスクリプトはNvidiaのGPUの使用する前提で組んでいます。CPUのみでのエンコードには対応していないので、対応させたい場合はAmatsukazeのオプションパラメータを調整してください。NVEncC64.exeについては各環境に合わせてパスを変更してください。
このスクリプトは下記を考慮しています。
- ファイルの最終書き込み日時と作成日時を比較して、変換・コピー処理が不要な場合はスキップする
- 録画中のM2TSはバックアップしない
- 番宣などの再生時間が極端に短いファイルはNASへのコピーはしない
- ファイルコピー途中に強制終了した場合に次回実行時に再度コピーを行う
- 変換途中に強制終了させた場合は次回実行時に再度変換処理を行う
- ファイル名の先頭に’_’をつけることで、変換・コピーの対象から外す
- 撮り貯め対象外フォルダの一部フォルダからのコピー処理をサポートする
- ローカルディスクとNAS上のファイルをミラーリングする
- mp4ファイルそのものに字幕データを保持する
ファイルは必ずBOM付きUTF-8で保存してください。BOMなしの場合、PowerShell内の日本語がShift JISとして認識されてしまい、PowerShellの処理文字コードであるUTF-8と食い違いが発生して正しく処理されません。
参考までに、4070Tiを使用している場合でCM抜きとmp4へのエンコードにかかる時間は、CM付きの2時間映画で12分~13分程度です。
@echo off
@REM Initialize batch
SET CURRENT_DIR_COPY_ENCODE_BACKUP_SHUTDOWN_BAT=%~dp0
cd /d %CURRENT_DIR_COPY_ENCODE_BACKUP_SHUTDOWN_BAT%
SET BATCH_NAME_COPY_ENCODE_BACKUP_SHUTDOWN_BAT=%~n0%~x0
@REM Get date/time string
for /f %%a in ('wmic os get LocalDateTime ^| findstr \.') DO SET LDT=%%a
SET CUR_DATE=%LDT:~0,8%
SET CUR_TIME=%LDT:~8,6%
@REM Constant
SET POWERSHELL_SCRIPT_PATH=%CURRENT_DIR_COPY_ENCODE_BACKUP_SHUTDOWN_BAT%\Copy_Encode_Backup_Shutdown.ps1
SET LOG_FILE_PATH_PATH=%CURRENT_DIR_COPY_ENCODE_BACKUP_SHUTDOWN_BAT%\%~n0_%CUR_DATE%_%CUR_TIME%.log
SET POWERSHELL_SCRIPT_ARGUMENT_LIST=^
-backup_m2ts ^
-backup_m2ts_within_day 0 ^
-convert_to_mp4 ^
-copy_to_nas ^
-generate_remoteplay ^
-remoteplay_synology ^
-log_file "%LOG_FILE_PATH_PATH%"
if "%1"=="" (
@echo [%BATCH_NAME_COPY_ENCODE_BACKUP_SHUTDOWN_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT_PATH% -ArgumentList %POWERSHELL_SCRIPT_ARGUMENT_LIST%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT_PATH% -ArgumentList %POWERSHELL_SCRIPT_ARGUMENT_LIST%
) else (
@echo [%BATCH_NAME_COPY_ENCODE_BACKUP_SHUTDOWN_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT_PATH% -ArgumentList %POWERSHELL_SCRIPT_ARGUMENT_LIST%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT_PATH% -ArgumentList %POWERSHELL_SCRIPT_ARGUMENT_LIST%
)
pause
Param(
[switch]$backup_m2ts,
[int]$backup_m2ts_within_day,
[switch]$convert_to_mp4,
[switch]$copy_to_nas,
[switch]$generate_remoteplay,
[switch]$remoteplay_rename,
[string]$amatsukaze_directory,
[string]$work_directory,
[string]$log_file,
[switch]$quiet,
[switch]$delete,
[switch]$shutdown,
[switch]$debug,
[switch]$help
)
#########################
# Parameters
#########################
[string]$script:script_path = Split-Path -Parent $PSCommandPath
[string]$script:script_name = Split-Path -Leaf $PSCommandPath
[string]$script:log_file_path=$log_file
if([string]::IsNullOrEmpty($script:log_file_path)){
$script:log_file_path = $PSCommandPath.Replace(".ps1",((Get-Date).ToString("_yyyyMMdd_HHmmss"))+".log")
}
[string[]]$script:source_directory_root = @()
$script:source_directory_root += "\\192.168.1.7\recorded_files\keep"
$script:source_directory_root += "\\192.168.1.7\recorded_files\on_hold"
# $script:source_directory_root += "\\192.168.1.6\tv-recorder\keep"
# $script:source_directory_root += "\\192.168.1.6\tv-recorder\on_hold"
[string[]]$script:tentative_directory_root = @()
$script:tentative_directory_root += "\\192.168.1.7\recorded_files\delete\7_映画(午後のロードショー)"
$script:tentative_directory_root += "\\192.168.1.6\tv-recorder\delete\7_映画(午後のロードショー)"
[string]$script:backup_directory_root = "Z:\tv-recorder\m2ts"
[string]$script:convert_directory_root = "Z:\tv-recorder\mp4"
[string]$script:nas_directory_root = "\\192.168.1.7\converted_files\mp4"
[string]$script:remote_play_directory_root = "Z:\tv-recorder\remote_play"
[string]$script:remote_play_directory_nas_root = "\\192.168.1.7\converted_files\remote_play"
[bool]$script:quiet_flag=$quiet
if($debug){
[string[]]$script:source_directory_root = @( "Z:\tv-recorder_test\recorded" )
[string] $script:backup_directory_root = "Z:\tv-recorder_test\m2ts"
[string] $script:convert_directory_root = "Z:\tv-recorder_test\mp4"
[string] $script:nas_directory_root = "Z:\tv-recorder_test\nas"
[string] $script:remote_play_directory_root = "Z:\tv-recorder_test\remote_play"
[string] $script:remote_play_directory_nas_root = "\\bluesky-nas\video\TV"
if( $backup_m2ts )
{
foreach( $source_directory in ${script:source_directory_root} )
{
if( -Not(Test-Path "${source_directory}") ){
New-Item -Type Directory ${source_directory} > $null
}
}
}
if( $backup_m2ts -and -Not(Test-Path "${script:backup_directory_root}") ){
New-Item -Type Directory ${script:backup_directory_root} > $null
}
if( $convert_to_mp4 -and -Not(Test-Path "${script:convert_directory_root}") ){
New-Item -Type Directory ${script:convert_directory_root} > $null
}
if( $copy_to_nas -and -Not(Test-Path "${script:nas_directory_root}") ){
New-Item -Type Directory ${script:nas_directory_root} > $null
}
if( $generate_remoteplay -and -Not(Test-Path "${script:remote_play_directory_root}") ){
New-Item -Type Directory ${script:remote_play_directory_root} > $null
}
if( $generate_remoteplay -and -Not(Test-Path "${script:remote_play_directory_nas_root}") ){
New-Item -Type Directory ${script:remote_play_directory_nas_root} > $null
}
}
[string]$script:amatsukaze_directory = $amatsukaze_directory
if( [string]::IsNullOrEmpty($script:amatsukaze_directory) ){
$script:amatsukaze_directory = "$script:script_path"
}
[string]$script:amatsukaze_cli_exe = "$script:amatsukaze_directory\exe_files\AmatsukazeCLI.exe"
[string]$script:nvencc64_exe = "$script:amatsukaze_directory\exe_files\NVEncC\NVEncC64.exe"
[string]$script:work_directory = $work_directory
if( [string]::IsNullOrEmpty($script:work_directory) ){
$script:work_directory = "$script:amatsukaze_directory\temp"
}
[string]$script:convert_to_mp4_commandline = " -s 1024" `
+ " --drcs `"$script:amatsukaze_directory\drcs\drcs_map.txt`" "`
+ " -w `"$script:work_directory`" "`
+ " --chapter-exe `"$script:amatsukaze_directory\exe_files\chapter_exe.exe`" "`
+ " --jls `"$script:amatsukaze_directory\exe_files\join_logo_scp.exe`" "`
+ " --cmoutmask 2 "`
+ " -et NVEnc "`
+ " -e `"$script:amatsukaze_directory\exe_files\NVEncC\NVEncC64.exe`" "`
+ " -j `"$script:work_directory\2-enc.json`" "`
+ " --mp4box `"$script:amatsukaze_directory\exe_files\mp4box.exe`" "`
+ " -t `"$script:amatsukaze_directory\exe_files\timelineeditor.exe`" "`
+ " -eo `"-c hevc --profile main10 --vbrhq 0 --vbr-quality 25 --gop-len 90 --cqp 20:23:25`" "`
+ " -fmt mp4 "`
+ " -m `"$script:amatsukaze_directory\exe_files\muxer.exe`" "`
+ " -bcm 0.5 "`
+ " --chapter "`
+ " -f `"$script:amatsukaze_directory\avscache\17E37FEB.avs`" "`
+ " --subtitles "`
+ " --jls-cmd `"$script:amatsukaze_directory\JL\JL_Standard.txt`" "`
+ " --mpeg2decoder CUVID "`
+ " --h264decoder CUVID "`
+ " --ignore-no-drcsmap "`
+ " --no-delogo "`
+ " --logo `"$script:amatsukaze_directory\logo\SID171-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1032-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1040-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1048-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1056-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1064-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1072-1.lgd`" "`
+ " --logo `"$script:amatsukaze_directory\logo\SID1072-2.lgd`" "`
[string]$script:remoteplay_commandline_nvenc= " -c h264 "`
+ " --avhw "`
+ " --profile main10 "`
+ " --vbrhq 0 "`
+ " --vbr-quality 25 "`
+ " --gop-len 90 "`
+ " --cqp 28:30:32 "`
+ " --max-bitrate 1000 "`
+ " --output-res 1280x720 "`
+ " --chapter-copy "`
+ " --audio-copy 1 "`
+ " --sub-copy "`
[int]$delay_shutdown = 180
[int]$recording_detection_duration = 1*60 #Unit:sec
[int]$copy_allow_size = 30*1000*1000 #Unit:byte
[int]$copy_allow_size_remote_play = 5*1000*1000 #Unit:byte
#########################
# Help
#########################
if( $help ) {
Write-Host "NAME"
Write-Host ""
Write-Host " $script:script_name - M2TSファイルをCMを抜いたMP4形式に変換しNASへコピーする"
Write-Host ""
Write-Host "SYPNOSYS"
Write-Host ""
Write-Host " $script:script_name [-amatsukaze_directory <directory path>]"
Write-Host " [-backup_m2ts] [-convert_to_mp4] [-copy_to_nas] [-generate_remoteplay] [-shutdown] [-strict]"
Write-Host " [-work_directory <directory path>] [-log_file <file path>] "
Write-Host ""
Write-Host "DESCRIPTION"
Write-Host ""
Write-Host " -amatsukaze_directory"
Write-Host " Amatsukazeがインストールされているディレクトリを指定する。指定がない場合はデフォルトパスが設定される "
Write-Host " デフォルトパス:${script:script_path}"
Write-Host ""
Write-Host " -work_directory"
Write-Host " Amatsukazeの一時ファイルの保存ディレクトリを指定する(高速なSSDを推奨)。指定がない場合はデフォルトパスが設定される "
Write-Host " デフォルトパス:${script:script_path}\temp"
Write-Host ""
Write-Host " -backup_m2ts"
Write-Host " M2TSファイルのバックアップコピーを実行する。"
Write-Host " コピー元:${script:source_directory_root}"
Write-Host " コピー先:${script:backup_directory_root}"
Write-Host ""
Write-Host " -backup_m2ts_within_day <days>"
Write-Host " M2TSファイルをコピーする際に、ここで指定された日数を経過したものはバックアップから除外する。0の場合はすべてコピーする"
Write-Host ""
Write-Host " -convert_to_mp4"
Write-Host " M2TSファイルのバックアップコピー元にあるファイルからCMを抜いてMP4へ変換する "
Write-Host " コピー元:${script:backup_directory_root}"
Write-Host " コピー先:${script:convert_directory_root}"
Write-Host ""
Write-Host " -copy_to_nas"
Write-Host " 変換したMP4ファイルをNASへコピーする"
Write-Host " コピー元:${script:convert_directory_root}"
Write-Host " コピー先:${script:nas_directory_root}"
Write-Host ""
Write-Host " -generate_remoteplay"
Write-Host " 変換したMP4ファイルをRemotePlay用にサイズを縮小して変換しNASへコピーする "
Write-Host " コピー元:${script:convert_directory_root}"
Write-Host " 変換先 :${script:remote_play_directory_root}"
Write-Host " コピー先:${script:remote_play_directory_nas_root}"
Write-Host ""
Write-Host " -remoteplay_rename"
Write-Host " RemotePlay用に録画日をファイル名の先頭から後ろに移動酢する[<ファイル名>(YYYYMMDD)] "
Write-Host " ※m2tsのファイル名は'YYYYMMDD <ファイル名>'である必要がある"
Write-Host ""
Write-Host " -quiet"
Write-Host " 処理開始前の確認をスキップします"
Write-Host ""
Write-Host " -delete"
Write-Host " PC上にないファイルをNASから削除します。quietオプションは無視されます"
Write-Host ""
Write-Host " -shutdown"
Write-Host " 変換終了後、${delay_shutdown}秒後にPCをシャットダウンする"
Write-Host ""
exit
}
###################
# Enumuration
###################
enum OutputLogType {
Info
Warning
Error
Debug
NoType
}
###################
# Class
###################
###################
# Function
###################
function OutputLog {
Param(
[OutputLogType]$type = [OutputLogType]::NoType,
[string]$function = $null,
[string]$message
)
[string]$output = ""
if([string]::IsNullOrEmpty($function)){
$output += [string]::Format("[${script:script_name}] " )
} else {
$output += [string]::Format("[${script:script_name}:${function}] " )
}
$output += [string]::Format("{0} : ", ((Get-Date).ToString("yyyy/MM/dd HH:mm:ss")) )
if( $type -ne [OutputLogType]::NoType ){
$output += [string]::Format("{0} : ", $type )
}
$output += [string]::Format("{0} ", $message )
if([string]::IsNullOrEmpty($script:log_file_path)){
Write-Output $output
} else {
Write-Output $output | Tee-Object -Append -FilePath $script:log_file_path
}
}
function ConvertPath([string]$filepath){
[string]$directory = ( Split-Path -Parent $filepath )
[string]$filename = ( Split-Path -Leaf $filepath )
[string]$modified_filename = $filename.Replace('[','`[').Replace(']','`]')
return $directory+"\" + $modified_filename + $filename_extention
}
function GetDurationString([TimeSpan]$duration){
[string]$duration_string=""
if($duration.TotalSeconds -lt 60){
$duration_string=($duration.TotalMilliseconds/1000).ToString("0.00")+" sec."
} else {
$duration_string+=($duration.TotalSeconds.ToString("0")/60).ToString("0")+" min. "
$duration_string+=($duration.TotalSeconds.ToString("0")%60).ToString("0")+" sec. "
}
return $duration_string
}
function BackupM2ts {
Param(
[string[]]$sources,
[string[]]$tentatives,
[string]$destination
)
[string]$output_message = "Start backup"
foreach( $source in $sources )
{
$output_message += "`n - ${source}"
}
OutputLog -type Info -function "BackupM2ts" -message $output_message
[string[]]$copy_files = @()
[string[]]$destination_files = @()
foreach( $source in $sources )
{
$m2ts_files = Get-ChildItem "$source" -Recurse -Filter *.m2ts -Exclude "lost+found"
foreach( $m2ts_file in $m2ts_files ){
if( !$m2ts_file.Name.StartsWith("_") ){
[bool]$copy_flag = $false
$destination_file_path = ConvertPath($m2ts_file.FullName.Replace($source,$destination))
if(-Not(Test-Path $destination_file_path) ) {
$copy_flag = $true
} elseif( ( (Get-Item $destination_file_path).Length -gt $m2ts_file.Length ) ){
$copy_flag = $true
}
if($copy_flag)
{
[string]$temp_file_path = (Split-Path -Parent ${destination_file_path}) + "\TEMP_" +(Split-Path -Leaf ${destination_file_path})
if( Test-Path( $temp_file_path) ){
Remove-Item -Path ${temp_file_path}
}
[timespan]$time_diff = [DateTime]::Now - $m2ts_file.LastWriteTime
if( $time_diff.TotalSeconds -lt $recording_detection_duration ){
$copy_flag = $false
} elseif ( (${backup_m2ts_within_day} -gt 0 ) -and ( $time_diff.TotalDays -ge $backup_m2ts_within_day ) ){
$copy_flag = $false
}
else
{
$find_flag = $false
foreach( $destination_file in $destination_files )
{
if( ( Split-Path -Leaf $m2ts_file ).Equals( ( Split-Path -Leaf $destination_file ) ) )
{
$find_flag = $true
break
}
}
if( !$find_flag )
{
$copy_files += $m2ts_file.FullName
$destination_files += $m2ts_file.FullName.Replace($source,$destination)
}
}
}
}
}
}
foreach( $tentative in $tentatives )
{
$m2ts_files = Get-ChildItem "$tentative" -Recurse -Filter *.m2ts -Exclude "lost+found"
$tentative_parent = (Split-Path -Parent $tentative)
foreach( $m2ts_file in $m2ts_files ){
if( !$m2ts_file.Name.StartsWith("_") ){
[bool]$copy_flag = $false
$destination_file_path = ConvertPath($m2ts_file.FullName.Replace($tentative_parent,$destination))
if(-Not(Test-Path $destination_file_path) )
{
$copy_flag = $true
} elseif( (Get-Item $destination_file_path).Length -ne $m2ts_file.Length ){
$copy_flag = $true
}
if($copy_flag)
{
[timespan]$time_diff = [DateTime]::Now - $m2ts_file.LastWriteTime
if( $time_diff.TotalSeconds -lt $recording_detection_duration ){
$copy_flag = $false
} elseif ( (${backup_m2ts_within_day} -gt 0 ) -and ( $time_diff.TotalDays -ge $backup_m2ts_within_day ) ){
$copy_flag = $false
}
else
{
$find_flag = $false
foreach( $destination_file in $destination_files )
{
if( ( Split-Path -Leaf $m2ts_file ).Equals( ( Split-Path -Leaf $destination_file ) ) )
{
$find_flag = $true
break
}
}
if( !$find_flag )
{
$copy_files += $m2ts_file.FullName
$destination_files += $m2ts_file.FullName.Replace($tentative_parent,$destination)
}
}
}
}
}
}
if( $copy_files.Count -gt 0 )
{
[string]$console_output = "以下のファイルをバックアップします。"
foreach( $copy_file in $copy_files ){
$console_output += ( "`n - ${copy_file}" )
}
OutputLog -type Info -function "BackupM2ts" -message $console_output
if( !$script:quiet_flag )
{
$response = ( Read-Host "よろしいですか?([Y]es/[n]o/[q]uiet)" )
switch($response)
{
"" {}
"y" {}
"q" {
$script:quiet_flag = $true
}
default { return; }
}
}
}
for( [int]$i=0; $i -lt $copy_files.Count; $i++ )
{
BackupM2ts_OneFile -source $copy_files[$i] -destination $destination_files[$i]
}
OutputLog -type Info -function "BackupM2ts" -message "Finish backup"
}
function BackupM2ts_OneFile {
Param(
[string]$source,
[string]$destination
)
[string]$source_file_name = ( Split-Path -Leaf $source )
[string]$source_file_path = ConvertPath($source)
[string]$destination_directory = Split-Path -Parent $destination
if( -Not(Test-Path(${destination_directory}))){
New-Item -Type Directory ($destination_directory) > $null
}
if( Test-Path(ConvertPath(${destination})) ){
Remove-Item -Path (ConvertPath(${destination}))
}
[UInt64]$source_size = (Get-Item $source_file_path).Length
OutputLog -type Info -function "BackupM2ts_OneFile" -message ( "Copy `""+$source_file_name+"`" ("+($source_size/1000/1000/1000).ToString("0.00")+"GB) ..." )
[DateTime]$start_time = [DateTime]::Now
if( $debug ){ OutputLog -type Debug -function "BackupM2ts_OneFile" -message "Copy-Item -Path $source_file_path -Destination $destination_directory -Force" }
[string]$temp_destination_path = ("$destination_directory"+"\TEMP_"+$source_file_name)
if( Test-Path(ConvertPath(${temp_destination_path})) ){
Remove-Item -Path (${temp_destination_path})
}
Copy-Item -Path "$source_file_path" -Destination ${temp_destination_path} -Force
if( Test-Path(ConvertPath(${destination})) ){
Remove-Item -Path (ConvertPath(${destination}))
}
Move-Item -Path (ConvertPath(${temp_destination_path})) -Destination ${destination} -Force
[TimeSpan]$duration=[DateTime]::Now-$start_time
OutputLog -type Info -function "BackupM2ts_OneFile" -message ( "Copy duration : " + (GetDurationString($duration)) )
[string]$destination_file_path = ConvertPath($destination)
if(-Not(Test-Path($destination_file_path))){
OutputLog -type Error -function "BackupM2ts_OneFile" -message ( "Fail to backup : " + $source_file_name )
}
}
function ConvertToMp4 {
Param(
[string]$source,
[string]$destination
)
OutputLog -type Info -function "ConvertToMp4" -message "Start converting"
$m2ts_files = Get-ChildItem "$source" -Recurse -Filter *.m2ts
$convert_files = @()
foreach( $m2ts_file in $m2ts_files ){
[bool]$convert_flag = $false
if( !$m2ts_file.Name.StartsWith("_") -and !$m2ts_file.Name.StartsWith("TEMP_") )
{
[string]$destination_file_path = ConvertPath($m2ts_file.FullName.Replace($source,$destination)).Replace(".m2ts",".mp4")
if( -Not(Test-Path $destination_file_path) ){
$convert_flag = $true
} elseif( (Get-Item $destination_file_path).LastWriteTime -lt $m2ts_file.CreationTime ){
$convert_flag = $true
} elseif( Test-Path( ConvertPath((Split-Path -Parent ${destination_file_path}) + "\TEMP_" +(Split-Path -Leaf ${destination_file_path}) ) ) ){
$convert_flag = $true
}
}
if($convert_flag){
[string]$temp_file_path = (Split-Path -Parent ${destination_file_path}) + "\TEMP_" +(Split-Path -Leaf ${destination_file_path})
if( Test-Path( $temp_file_path) ){
Remove-Item -Path ${temp_file_path}
}
$convert_files += $m2ts_file
}
}
if( $convert_files.Count -gt 0 )
{
[string]$console_output = "以下のファイルをMP4へ変換します。"
foreach( $convert_file in $convert_files ){
$console_output += ( "`n - " + (Split-Path -Leaf $convert_file) )
}
OutputLog -type Info -function "ConvertToMp4" -message $console_output
if( !$script:quiet_flag )
{
$response = ( Read-Host "よろしいですか?([Y]es/[n]o/[q]uiet)" )
switch($response)
{
"" {}
"y" {}
"q" {
$script:quiet_flag = $true
}
default { return; }
}
}
}
foreach( $convert_file in $convert_files )
{
ConvertToMp4_OneFile -source $convert_file.FullName -destination $convert_file.FullName.Replace($source,$destination).Replace(".m2ts",".mp4")
}
OutputLog -type Info -function "ConvertToMp4" -message "Finish converting"
Remove-Item "${script:work_directory}/*" -Exclude .gitkeep -Recurse -Force > $null
}
function ConvertToMp4_OneFile {
Param(
[string]$source,
[string]$destination
)
[string]$source_file_name = ( Split-Path -Leaf $source )
[string]$destination_directory = (Split-Path -Parent $destination)
if( -Not(Test-Path(${destination_directory}))){
New-Item -Type Directory ${destination_directory} > $null
}
OutputLog -type Info -function "ConvertToMp4_OneFile" -message "Convert `"$source_file_name`" ..."
[DateTime]$start_time_convert = [DateTime]::Now
if( Test-Path(ConvertPath(${destination})) ){
Remove-Item -Path (ConvertPath(${destination}))
}
if( Test-Path(ConvertPath(${destination}).Replace(".mp4",".ass")) ){
Remove-Item -Path (ConvertPath(${destination}).Replace(".mp4",".ass"))
}
[string]$temp_file = (Split-Path -Parent ${destination}) + "\TEMP_" +(Split-Path -Leaf ${destination})
[string]$argument_list = $script:convert_to_mp4_commandline
$argument_list += " --input `"${source}`" "
$argument_list += " --output `"${temp_file}`" "
OutputLog -type Info -function "ConvertToMp4_OneFile" -message "Start-Process $script:amatsukaze_cli_exe -ArgumentList `"$argument_list`" -Wait"
Start-Process $script:amatsukaze_cli_exe -ArgumentList "$argument_list" -Wait
Move-Item -Path (ConvertPath(${temp_file})) -Destination (ConvertPath(${destination}))
if( Test-Path(ConvertPath(${temp_file}).Replace(".mp4",".ass")) ){
Move-Item -Path (ConvertPath(${temp_file}).Replace(".mp4",".ass")) -Destination (ConvertPath(${destination}).Replace(".mp4",".ass"))
}
[TimeSpan]$duration_convert=[DateTime]::Now-$start_time_convert
OutputLog -type Info -function "ConvertToMp4_OneFile" -message ( "Convert duration : " + (GetDurationString($duration_convert) ) )
[string]$destination_file_path = ConvertPath($destination)
if(-Not(Test-Path($destination_file_path))){
OutputLog -type Error -function "ConvertToMp4_OneFile" -message ( "Fail to convert : " + $source_file_name )
}
}
function CopyToNas {
Param(
[string]$source,
[string]$destination,
[int]$minimum_copy_size
)
OutputLog -type Info -function "CopyToNas" -message "Start copying to NAS"
$mp4_files = Get-ChildItem "$source" -Recurse -Filter *.mp4
$copy_files = @()
foreach( $mp4_file in $mp4_files ){
[bool]$copy_flag = $false
if( !$mp4_file.Name.StartsWith("_")-and !$mp4_file.Name.StartsWith("TEMP_") )
{
[string]$destination_file_path = ConvertPath($mp4_file.FullName.Replace($source,$destination))
if(-Not(Test-Path $destination_file_path) ){
$copy_flag = $true
} elseif( (Get-Item $destination_file_path).Length -ne $mp4_file.Length ){
$copy_flag = $true
}
if($copy_flag)
{
if( $mp4_file.Length -gt $minimum_copy_size )
{
$copy_files += $mp4_file
}
}
}
}
if( $copy_files.Count -gt 0 )
{
[string]$console_output = "以下のファイルをNASへコピーします。"
foreach( $copy_file in $copy_files ){
$console_output += ( "`n - ${copy_file}" )
}
OutputLog -type Info -function "CopyToNas" -message $console_output
if( !$script:quiet_flag )
{
$response = ( Read-Host "よろしいですか?([Y]es/[n]o/[q]uiet)" )
$response = ( Read-Host $console_output ).Trim()
switch($response)
{
"" {}
"y" {}
"q" {
$script:quiet_flag = $true
}
default { return; }
}
}
}
foreach( $copy_file in $copy_files )
{
CopyToNas_OneFile -source $copy_file.FullName -destination $copy_file.FullName.Replace($source,$destination)
}
OutputLog -type Info -function "CopyToNas" -message "Finish copying to NAS"
}
function CopyToNas_OneFile {
Param(
[string]$source,
[string]$destination
)
[string]$source_file_path = ConvertPath($source)
[string]$source_file_name = ( Split-Path -Leaf $source )
[string]$destination_directory = Split-Path -Parent $destination
if( -Not(Test-Path(${destination_directory}))){
New-Item -Type Directory ($destination_directory) > $null
}
[UInt64]$source_size = (Get-Item $source_file_path).Length
OutputLog -type Info -function "CopyToNas_OneFile" -message ( "Copy `""+$source_file_name+"`" ("+($source_size/1000/1000/1000).ToString("0.00")+"GB) ..." )
[DateTime]$start_time = [DateTime]::Now
if( $debug ){ OutputLog -type Debug -function "CopyToNas_OneFile" -message "Copy-Item -Path $source_file_path -Destination $destination_directory -Force" }
[string]$temp_destination_path = ("$destination_directory"+"\TEMP_"+$source_file_name)
if( Test-Path(ConvertPath(${temp_destination_path})) ){
Remove-Item -Path (${temp_destination_path})
}
Copy-Item -Path "$source_file_path" -Destination ${temp_destination_path} -Force
if( Test-Path(ConvertPath(${destination})) ){
Remove-Item -Path (ConvertPath(${destination}))
}
Move-Item -Path (ConvertPath(${temp_destination_path})) -Destination ${destination} -Force
if( Test-Path($source_file_path.Replace(".mp4",".ass")) ){
Copy-Item -Path $source_file_path.Replace(".mp4",".ass") -Destination $destination_directory -Force
}
[TimeSpan]$duration=[DateTime]::Now-$start_time
OutputLog -type Info -function "CopyToNas_OneFile" -message ( "Copy duration : " + (GetDurationString($duration)) )
[string]$destination_file_path = ConvertPath($destination)
if(-Not(Test-Path($destination_file_path))){
OutputLog -type Error -function "CopyToNas_OneFile" -message ( "Fail to convert : " + $source_file_name )
}
}
function GenerateRemotePlay_ConvertFileName {
Param(
[string]$filepath
)
if(!$remoteplay_rename)
{
return $filepath
}
[string]$directory = ( Split-Path -Parent $filepath )
[string]$filename = ( Split-Path -Leaf $filepath )
[string]$filename_body = [System.IO.Path]::GetFileNameWithoutExtension($filename)
[string]$filename_extention = [System.IO.Path]::GetExtension($filename)
[string]$date = $filename_body.Substring(0,8)
[string]$modified_filename_body = ( $filename_body.Substring(9) -Replace "\[.\]", "" )+ "(" + $date + ")"
return $directory+"\" + $modified_filename_body + $filename_extention
}
function GenerateRemotePlay {
Param(
[string]$source,
[string]$destination
)
OutputLog -type Info -function "GenerateRemotePlay" -message "Start generating remote play file"
$mp4_files = Get-ChildItem "$source" -Recurse -Filter *.mp4
[string[]]$convert_files = @()
[string[]]$destination_files = @()
foreach( $mp4_file in $mp4_files )
{
[bool]$convert_flag = $false
if( !$mp4_file.Name.StartsWith("_") -and !$mp4_file.Name.StartsWith("TEMP_") ){
[string]$destination_file_path = GenerateRemotePlay_ConvertFileName($mp4_file.FullName.Replace($source,$destination))
if( -Not(Test-Path (ConvertPath($destination_file_path)) )){
$convert_flag = $true
} elseif( (Get-Item (ConvertPath($destination_file_path)) ).LastWriteTime -lt $mp4_file.LastWriteTime ){
$convert_flag = $true
} elseif( Test-Path( ConvertPath((Split-Path -Parent (ConvertPath(${destination_file_path}))) + "\TEMP_" +(Split-Path -Leaf ${destination_file_path}) ) ) ){
$convert_flag = $true
}
if($convert_flag)
{
if( $mp4_file.Length -gt $copy_allow_size )
{
[string]$temp_file_path = (Split-Path -Parent ${destination_file_path}) + "\TEMP_" +(Split-Path -Leaf (GenerateRemotePlay_ConvertFileName($mp4_file.FullName)))
if( Test-Path( ConvertPath($temp_file_path)) ){
Remove-Item -Path (ConvertPath(${temp_file_path}))
}
$convert_files += $mp4_file.FullName
$destination_files += $mp4_file.FullName.Replace($source,$destination)
}
}
}
}
if( $convert_files.Count -gt 0 )
{
[string]$console_output = "以下のファイルをリモート視聴用ファイルに変換します。"
foreach( $convert_file in $convert_files )
{
$console_output += ( "`n - " + (Split-Path -Leaf $convert_file) + "`n -> " + (Split-Path -Leaf (GenerateRemotePlay_ConvertFileName($convert_file))) )
}
OutputLog -type Info -function "GenerateRemotePlay" -message $console_output
if( !$script:quiet_flag )
{
$response = ( Read-Host "よろしいですか?([Y]es/[n]o/[q]uiet)" )
switch($response)
{
"" {}
"y" {}
"q" {
$script:quiet_flag = $true
}
default { return; }
}
}
}
for( [int]$i=0; $i -lt $convert_files.Count; $i++ )
{
GenerateRemotePlay_OneFile -source $convert_files[$i] -destination $destination_files[$i]
}
Remove-Item "${script:work_directory}/*" -Exclude .gitkeep -Recurse -Force > $null
OutputLog -type Info -function "GenerateRemotePlay" -message "Finish generating remote play file"
}
function GenerateRemotePlay_OneFile {
Param(
[string]$source,
[string]$destination
)
[string]$source_file_name = ( Split-Path -Leaf $source )
[string]$source_file_path = ConvertPath($source)
[string]$destination_directory = (Split-Path -Parent $destination)
if( -Not(Test-Path(${destination_directory}))){
New-Item -Type Directory ${destination_directory} > $null
}
OutputLog -type Info -function "GenerateRemotePlay_OneFile" -message "Convert `"$source_file_name`" ..."
[DateTime]$start_time_convert = [DateTime]::Now
if( Test-Path(ConvertPath(${destination})) ){
Remove-Item -Path (ConvertPath(${destination}))
}
if( Test-Path(ConvertPath(${destination}).Replace(".mp4",".ass")) ){
Remove-Item -Path (ConvertPath(${destination}).Replace(".mp4",".ass"))
}
[string]$temp_file = (Split-Path -Parent ${destination}) + "\TEMP_" +(Split-Path -Leaf ${destination})
[string]$argument_list = $script:remoteplay_commandline_nvenc
$argument_list += " --input `"${source}`" "
$argument_list += " --output `"${temp_file}`" "
OutputLog -type Info -function "GenerateRemotePlay_OneFile" -message "Start-Process $script:nvencc64_exe -ArgumentList `"$argument_list`" -Wait"
Start-Process $script:nvencc64_exe -ArgumentList "$argument_list" -Wait
Move-Item -Path (ConvertPath(${temp_file})) -Destination (ConvertPath(${destination}))
if( Test-Path($source_file_path.Replace(".mp4",".ass")) ){
Copy-Item -Path $source_file_path.Replace(".mp4",".ass") -Destination $destination.Replace(".mp4",".ass") -Force
}
[TimeSpan]$duration_convert=[DateTime]::Now-$start_time_convert
OutputLog -type Info -function "GenerateRemotePlay_OneFile" -message ( "Convert duration : " + (GetDurationString($duration_convert) ) )
[string]$destination_file_path = ConvertPath($destination)
if(-Not(Test-Path($destination_file_path))){
OutputLog -type Error -function "GenerateRemotePlay_OneFile" -message ( "Fail to convert : " + $source_file_name )
}
}
function DeleteFiles {
Param(
[string]$source,
[string]$destination
)
OutputLog -type Info -function "DeleteFiles" -message "Start deleting files from share folder"
$mp4_files = Get-ChildItem "$destination" -Recurse -Filter *.mp4
[string[]]$delete_files = @()
foreach( $mp4_file in $mp4_files ){
if( !$mp4_file.Name.StartsWith("_") ){
$source_file_path = ConvertPath($mp4_file.FullName.Replace($destination,$source))
if(-Not(Test-Path $source_file_path) ) {
$delete_files += $mp4_file.FullName
}
}
}
if( $delete_files.Count -gt 0 )
{
[string]$console_output = "以下のファイルを共有フォルダから削除します。`n"
foreach( $delete_file in $delete_files )
{
$console_output += ( " - " + (Split-Path -Leaf $delete_file) + "`n" )
}
$console_output += "よろしいですか?([y]es/[N]o)"
$response = ( Read-Host $console_output ).Trim()
switch($response)
{
"y" {}
default { return; }
}
}
foreach( $delete_file in $delete_files )
{
Remove-Item -Path (ConvertPath($delete_file))
if( Test-Path(ConvertPath($delete_file).Replace(".mp4",".ass")) ){
Remove-Item -Path (ConvertPath($delete_file).Replace(".mp4",".ass"))
}
[string]$parent_folder_path = (Split-Path -Parent $delete_file)
[int]$number_of_mp4_file = (Get-ChildItem $parent_folder_path -Recurse -Filter *.mp4 -ErrorAction Stop | Measure-Object).Count
if ( $number_of_mp4_file -eq 0){
Remove-Item -Path (ConvertPath($parent_folder_path)) -Force -Recurse
}
}
}
#########################
# Parameter check
#########################
if( -Not(Test-Path "${script:amatsukaze_directory}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "No Amatsukaze folder '"+${script:amatsukaze_director}+"'")
exit
}
if( $backup_m2ts )
{
foreach( $source_directory in ${script:source_directory_root} )
{
if( -Not(Test-Path "${source_directory}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "No m2ts folder '"+${source_directory}+"'")
exit
}
}
foreach( $tentative_directory in ${script:tentative_directory_root} )
{
if( -Not(Test-Path "${tentative_directory}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "No m2ts folder '"+${tentative_directory_root}+"'")
exit
}
}
}
if( $copy_to_nas -and -Not(Test-Path "${script:nas_directory_root}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "No NAS folder '"+${script:nas_directory_root}+"'")
exit
}
if( $generate_remoteplay -and -Not(Test-Path "${script:remote_play_directory_nas_root}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "No NAS folder '"+${script:remote_play_directory_nas_root}+"'")
exit
}
if( -Not(Test-Path "${script:work_directory}") ){
New-Item -Type Directory ${script:work_directory} > $null
if( -Not(Test-Path "${script:work_directory}") ){
OutputLog -type Error -function "ParameterCheck" -message ( "Cannot create work directory '"+${script:work_directory}+"'")
exit
}
}
$log_file_dir = ( Split-Path -Parent ${script:log_file_path} )
if( -Not(Test-Path (${log_file_dir}) ) ){
New-Item -Type Directory ${log_file_dir} > $null
}
#########################
# Main
#########################
OutputLog -type Info -message "Start processing"
Remove-Item "${script:work_directory}/*" -Exclude .gitkeep -Recurse -Force > $null
# Backup m2ts
if( $backup_m2ts ){
if( -Not(Test-Path "${script:backup_directory_root}") ){
New-Item -Type Directory ${script:backup_directory_root} > $null
}
BackupM2ts -sources ${script:source_directory_root} -tentatives ${script:tentative_directory_root} -destination ${script:backup_directory_root}
}
# convert
if( $convert_to_mp4 ){
if( -Not(Test-Path "${script:convert_directory_root}") ){
New-Item -Type Directory ${script:convert_directory_root} > $null
}
ConvertToMp4 -source "$script:backup_directory_root" -destination "$script:convert_directory_root"
}
# Copy to NAS
if( $copy_to_nas ){
CopyToNas -source "$script:convert_directory_root" -destination "$script:nas_directory_root" -minimum_copy_size $copy_allow_size
}
# convert
if( $generate_remoteplay ){
if( -Not(Test-Path "${script:remote_play_directory_root}") ){
New-Item -Type Directory ${script:remote_play_directory_root} > $null
}
GenerateRemotePlay -source "$script:convert_directory_root" -destination "$script:remote_play_directory_root"
CopyToNas -source "$script:remote_play_directory_root" -destination "$script:remote_play_directory_nas_root" -minimum_copy_size $copy_allow_size_remote_play
}
# delete
if( $delete ){
DeleteFiles -source "$script:convert_directory_root" -destination "$script:nas_directory_root"
DeleteFiles -source "$script:remote_play_directory_root" -destination "$script:remote_play_directory_nas_root"
}
#shutdown
if( $shutdown ){
for( [int]$i=$delay_shutdown;$i -gt 0;$i-=10){
Write-Output ([string]::Format("[$script:script_name] {0} : Info : Shutdown in ${i} sec.", ((Get-Date).ToString("yyyy/MM/dd HH:mm:ss")) ) ) | Tee-Object -Append -FilePath $script:log_file_path
Start-Sleep -s 10
}
Write-Output ([string]::Format("[$script:script_name] {0} : Info : Shutdown", ((Get-Date).ToString("yyyy/MM/dd HH:mm:ss")) ) ) | Tee-Object -Append -FilePath $script:log_file_path
shutdown.exe /s -t 5
}
OutputLog -type Info -message "Finish processing"