Amatsukazeを利用してCM抜きMP4を自動生成する

RasberriPiで録画したM2TSファイルは1時間番組でもファイルサイズが7GBを超えるため、バックアップやコピーに時間がかかります。さらに再生用ソフトも限定されるため、モバイル端末での視聴にも制限がかかります。この問題は、MP4へ変換してファイルサイズを落とすことで解決できます。

MP4へ変換したときのファイルサイズは、スポーツ番組や旅番組など画像の変化の激しいものや古い映画などのようにノイズの多いものはM2TSの半分程度までしか落ちないこともありますが、アニメなどの変化の少ないものは10分の1程度まで落とせます。概ね2-3割程度まで落とせると考えればいいでしょう。

また、変換の際にCMを抜くとさらにファイルサイズを落とせます。通常2割弱程度がCMですので、圧縮後のファイルからさらに2割サイズを落とすことができますが、手動でCMカットはかなりの手間です。しかし、WindowsアプリのAmatsukazeを利用することでCM抜きエンコーディングを自動化できます。

Amatsukazeはコマンドラインからも実行できるのでバッチファイルによる複数ファイルの自動処理も可能ですが、番組録画はファイルの変換だけでなく、変換したファイルをNASへコピーしたり、古いファイルは削除してHDDの容量を確保したりと、自動化に際しては様々な要求が出てきます。

そこで、バッチファイルからPowershellを起動する形で処理を自動化します。Powershellはコピー・変換・削除の機能をそれぞれ実装し、バッチファイル側で機能を選択する形で様々なパターンに対応できるようにしました。PowershellはWindowsのデフォルトシェルなので、Pythonのように別途ソフトをインストールすることなく自動処理が実現できることが利点です。

Amatsukazeを利用してmp4への変換を行うPowershellスクリプトとスクリプト起動用のバッチファイルを以下に示します。具体的には以下の処理を実行するPowerShellスクリプトを用意し、バッチファイルでこれらの処理を組み合わせています。

  1. M2TSファイルのコピーをとる
  2. Amatsukazeを使ってCM抜き処理とmp4変換を実行する
  3. mp4とass(字幕ファイル)をコピーする
  4. 指定した日数が経過したファイルを削除する

このスクリプトは下記を考慮しています。

  • ファイルの最終書き込み日時と作成日時を比較して、変換・コピー処理が不要な場合はスキップする
  • コピー・変換中にスケジューラーなどで変換・コピー処理を再実行した場合は処理をスキップする
  • 録画中のM2TSはバックアップしない
  • ファイルコピー途中に強制終了した場合に次回実行時に再度コピーを行う
  • 変換途中に強制終了させた場合は次回実行時に再度変換処理を行う
  • ファイル名の先頭に’_’をつけることで、変換・コピーの対象から外す
  • mp4ファイルとともに生成される字幕データ(.ass)をコピー対象にする
  • ファイル削除時にフォルダが空になった場合はフォルダも削除する

ファイルは必ずBOM付きUTF-8で保存してください。BOMなしの場合、PowerShell内の日本語がShift JISとして認識されてしまい、PowerShellの処理文字コードであるUTF-8と食い違いが発生して正しく処理されません。

参考までに、RTX 4070TiでCM抜きとmp4へのエンコードにかかる時間は、CM付きの2時間映画で12分~13分程度です。N100の場合は1時間10分程度です。

@echo off

@REM Initialize batch
SET CURRENT_DIR_SAMPLE_BATCH_BAT=%~dp0
cd /d %CURRENT_DIR_SAMPLE_BATCH_BAT%
SET BATCH_NAME_SAMPLE_BATCH_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_DIR=%CURRENT_DIR_SAMPLE_BATCH_BAT%
SET LOG_FILE_PATH_PATH=%POWERSHELL_SCRIPT_DIR%\log\%CUR_DATE%_%CUR_TIME%_%~n0.log
SET POWERSHELL_SCRIPT_COPY_M2TS=%POWERSHELL_SCRIPT_DIR%CopyM2ts.ps1
SET POWERSHELL_SCRIPT_COPY_CONVERT=%POWERSHELL_SCRIPT_DIR%Convert.ps1
SET POWERSHELL_SCRIPT_COPY_MP4=%POWERSHELL_SCRIPT_DIR%CopyMp4.ps1
SET POWERSHELL_SCRIPT_DELETE_FILES=%POWERSHELL_SCRIPT_DIR%Delete.ps1

@REM Copy Record files to backup folder 
SET POWERSHELL_SCRIPT=%POWERSHELL_SCRIPT_COPY_M2TS%
SET ARGUMENT=
SET ARGUMENT= %ARGUMENT% -log_file "%LOG_FILE_PATH_PATH%"
SET ARGUMENT= %ARGUMENT% -source "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\m2ts"
SET ARGUMENT= %ARGUMENT% -destination "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\m2ts_backup"
@REM SET ARGUMENT= %ARGUMENT% -quiet
@REM SET ARGUMENT= %ARGUMENT% -delete
@REM SET ARGUMENT= %ARGUMENT% -shutdown
@echo [%BATCH_NAME_SAMPLE_BATCH_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%

@REM Convert files
SET POWERSHELL_SCRIPT=%POWERSHELL_SCRIPT_COPY_CONVERT%
SET ARGUMENT=
SET ARGUMENT= %ARGUMENT% -log_file "%LOG_FILE_PATH_PATH%"
SET ARGUMENT= %ARGUMENT% -source "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\m2ts"
SET ARGUMENT= %ARGUMENT% -destination "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\convert"
@REM SET ARGUMENT= %ARGUMENT% -ignore_no_logo
@REM SET ARGUMENT= %ARGUMENT% -nvenc
@REM SET ARGUMENT= %ARGUMENT% -quiet
@REM SET ARGUMENT= %ARGUMENT% -shutdown
@echo [%BATCH_NAME_SAMPLE_BATCH_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%

@REM Copy convert files
SET POWERSHELL_SCRIPT=%POWERSHELL_SCRIPT_COPY_MP4%
SET ARGUMENT=
SET ARGUMENT= %ARGUMENT% -log_file "%LOG_FILE_PATH_PATH%"
SET ARGUMENT= %ARGUMENT% -source "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\convert"
SET ARGUMENT= %ARGUMENT% -destination "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\convert_backup"
@REM SET ARGUMENT= %ARGUMENT% -quiet
@REM SET ARGUMENT= %ARGUMENT% -delete
@REM SET ARGUMENT= %ARGUMENT% -shutdown
@echo [%BATCH_NAME_SAMPLE_BATCH_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%

@REM Delete files
SET POWERSHELL_SCRIPT=%POWERSHELL_SCRIPT_DELETE_FILES%
SET ARGUMENT=
SET ARGUMENT= %ARGUMENT% -log_file "%LOG_FILE_PATH_PATH%"
SET ARGUMENT= %ARGUMENT% -source "%CURRENT_DIR_SAMPLE_BATCH_BAT%files\convert"
SET ARGUMENT= %ARGUMENT% -day 1
@REM SET ARGUMENT= %ARGUMENT% -quiet
@REM SET ARGUMENT= %ARGUMENT% -delete
@REM SET ARGUMENT= %ARGUMENT% -shutdown
@echo [%BATCH_NAME_SAMPLE_BATCH_BAT%] powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%
call powershell -NoProfile -ExecutionPolicy Bypass -file %POWERSHELL_SCRIPT% -ArgumentList %ARGUMENT%

pause
Param(
    [string]$log_file,
    [string]$source,
    [string]$destination,
    [switch]$quiet,
    [switch]$delete,
    [switch]$shutdown,
    [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 = $script:script_path+((Get-Date).ToString("\\yyyyMMdd_HHmmss_"))+$script_name.Replace(".ps1",".log")
}

[string]$script:source_root      = $source
[string]$script:destination_root = $destination

[bool]$script:quiet_flag=$quiet

[bool]$debug=$false
if($debug){
    [string]$script:source_root      = "${script:script_path}\files\m2ts"
    [string]$script:destination_root = "${script:script_path}\files\m2ts_backup"

    if( -Not(Test-Path "${script:destination_root}") ){
        New-Item -Type Directory ${script:destination_root} > $null
    }
}

[int]$recording_detection_duration = 1*60         #Unit:sec

#########################
# Help
#########################
if( $help ) {
    Write-Host "NAME"
    Write-Host ""
    Write-Host "   $script:script_name - M2TSファイルをコピーする"
    Write-Host ""
    Write-Host "SYPNOSYS"
    Write-Host ""
    Write-Host "   $script:script_name -log_file <file path>"
    Write-Host "                       -source <directory path>"
    Write-Host "                       -destination <directory path>"
    Write-Host "                       [-quiet] [-help]"
    Write-Host ""
    Write-Host "DESCRIPTION"
    Write-Host ""
    Write-Host "   -source"
    Write-Host "        M2TSファイルが格納されているディレクトリを指定する"
    Write-Host ""
    Write-Host "   -destination"
    Write-Host "        M2TSファイルのコピー先のディレクトリを指定する"
    Write-Host ""
    Write-Host "   -quiet"
    Write-Host "        処理開始前の確認をスキップする"
    Write-Host ""
    Write-Host "   -delete"
    Write-Host "        sourceにないファイルを削除する。quietオプションは無視される"
    Write-Host ""
    Write-Host "   -shutdown"
    Write-Host "        変換終了後、${delay_shutdown}秒後にPCをシャットダウンする"
    Write-Host ""
    Write-Host "   -help"
    Write-Host "        ヘルプを表示する"
    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
}

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 CopyM2ts {
    Param(
        [string]$source,
        [string]$destination
    )

    [string]$output_message = "Start backup M2TS"

    OutputLog -type Info -function "CopyM2ts" -message $output_message
        
    [string[]]$copy_files = @()
    [string[]]$destination_files = @()
    $m2ts_files = Get-ChildItem "$source" -Recurse -Filter *.m2ts -Exclude "lost+found"

    foreach( $m2ts_file in $m2ts_files ){

        if( !$m2ts_file.Name.StartsWith(".") -and !$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 -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 ){
                    OutputLog -type Info -function "CopyM2ts" -message ( "In Recording ..." + $m2ts_file.Name )
                    $copy_flag = $false
                } else {
                    [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}
                    }

                    $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)
                    }
                }
            } 
        }
    }

    if( $copy_files.Count -gt 0 )
    {
        [string]$console_output = "以下のファイルをコピーします。"
        foreach( $copy_file in $copy_files ){
            $console_output += ( "`n  - ${copy_file}" )
        }
        $console_output += "`n  コピー先:$destination"
        OutputLog -type Info -function "CopyM2ts" -message $console_output

        if( !$script:quiet_flag )
        {
            $response = ( Read-Host "よろしいですか?([Y]es/[n]o)" )
            switch($response)
            {
                "" {}
                "y" {}
                default { return; }
            }
        }
    }

    for( [int]$i=0; $i -lt $copy_files.Count; $i++ )
    {
        CopyM2ts_OneFile -source $copy_files[$i] -destination $destination_files[$i]
    }
    
    OutputLog -type Info -function "CopyM2ts" -message "Finish copying M2TS"
}

function CopyM2ts_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 "CopyM2ts_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 "CopyM2ts_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 "CopyM2ts_OneFile" -message ( "Copy duration : " + (GetDurationString($duration)) )

    [string]$destination_file_path = ConvertPath($destination)
    if(-Not(Test-Path($destination_file_path))){
        OutputLog -type Error -function "CopyM2ts_OneFile" -message ( "Fail to backup : " + $source_file_name  )
    }
}

function DeleteFiles {
    Param(
        [string]$source,
        [string]$destination
    )

    OutputLog -type Info -function "DeleteFiles" -message "Start deleting files from destination folder"

    $m2ts_files = Get-ChildItem "$destination" -Recurse -Filter *.m2ts

    [string[]]$delete_files = @()
    foreach( $m2ts_file in $m2ts_files ){

        if( !$m2ts_file.Name.StartsWith("_") ){

            $source_file_path = ConvertPath($m2ts_file.FullName.Replace($destination,$source))
    
            if(-Not(Test-Path $source_file_path) ) {
                $delete_files += $m2ts_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
        } 
    }
}

function CheckAlreadyRunning {
    [int]$number_of_process = (Get-Process -Name powershell | Where-Object -FilterScript {$_.Id -ne $PID}).Count

    return ($number_of_process -gt 0)
}

#########################
# Parameter check
#########################
if( [string]::IsNullOrEmpty("${script:source_root}") -or -Not(Test-Path "${script:source_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No source folder '"+${script:source_root}+"'")
    exit
}

if( [string]::IsNullOrEmpty("${script:destination_root}") -or -Not(Test-Path "${script:destination_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No destination folder '"+${script:destination_root}+"'")
    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 copying M2TS"
OutputLog -type Info -message "  コピー元フォルダ:${script:source_root}"
OutputLog -type Info -message "  コピー先フォルダ:${script:destination_root}"

if( CheckAlreadyRunning ){
    OutputLog -type Info -message "変換・コピー処理実行中のため中断します"
    exit
}

# Backup m2ts
if( -Not(Test-Path "${script:destination_root}") ){
    New-Item -Type Directory ${script:destination_root} > $null
}

CopyM2ts -source ${script:source_root} -destination ${script:destination_root}

# delete
if( $delete ){
    DeleteFiles -source "$script:source_root" -destination "$script:destination_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 copying M2TS"
Param(
    [string]$log_file,
    [string]$source,
    [string]$destination,
    [switch]$nvenc,
    [switch]$ignore_no_logo,
    [switch]$quiet,
    [switch]$shutdown,
    [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 = $script:script_path+((Get-Date).ToString("\\yyyyMMdd_HHmmss_"))+$script_name.Replace(".ps1",".log")
}

[string]$script:source_root      = $source
[string]$script:destination_root = $destination

[bool]$script:quiet_flag=$quiet
[bool]$script:nvenc_flag=$nvenc
[bool]$script:ignore_no_logo_flag=$ignore_no_logo

[bool]$debug=$false
if($debug){
    [string]$script:source_root      = "${script:script_path}\files\m2ts"
    [string]$script:destination_root = "${script:script_path}\files\convert"

    if( -Not(Test-Path "${script:source_root}") ){
        New-Item -Type Directory ${script:destination_root} > $null
    }
}

[string]$script:amatsukaze_directory = $script:script_path.Replace("\AutomationBatch","")
if( [string]::IsNullOrEmpty($script:amatsukaze_directory) ){
    $script:amatsukaze_directory = ""
}
[string]$script:amatsukaze_cli_exe = "$script:amatsukaze_directory\exe_files\AmatsukazeCLI.exe"
[string]$script:work_directory = "$script:amatsukaze_directory\temp"

[string]$script:convert_to_mp4_commandline = ""
$script:convert_to_mp4_commandline += " -s 1024"
$script:convert_to_mp4_commandline += " --drcs `"$script:amatsukaze_directory\drcs\drcs_map.txt`""
$script:convert_to_mp4_commandline += " -w `"$script:work_directory`""
$script:convert_to_mp4_commandline += " --chapter-exe `"$script:amatsukaze_directory\exe_files\chapter_exe.exe`""
$script:convert_to_mp4_commandline += " --jls `"$script:amatsukaze_directory\exe_files\join_logo_scp.exe`""
$script:convert_to_mp4_commandline += " --cmoutmask 2"
if( $script:nvenc_flag ){
    $script:convert_to_mp4_commandline += " -et NVEnc"
    $script:convert_to_mp4_commandline += " -e `"$script:amatsukaze_directory\exe_files\NVEncC\NVEncC64.exe`""
} else {
    $script:convert_to_mp4_commandline += " -et QSVEnc"
    $script:convert_to_mp4_commandline += " -e `"$script:amatsukaze_directory\exe_files\QSVEncC\QSVEncC64.exe`""
}
$script:convert_to_mp4_commandline += " -j `"$script:work_directory\2-enc.json`""
$script:convert_to_mp4_commandline += " --mp4box `"$script:amatsukaze_directory\exe_files\mp4box.exe`""
$script:convert_to_mp4_commandline += " -t `"$script:amatsukaze_directory\exe_files\timelineeditor.exe`""
if( $script:nvenc_flag ){
    $script:convert_to_mp4_commandline += " -eo `"-c hevc --profile main10 --vbrhq 0 --vbr-quality 25 --gop-len 90 --cqp 20:23:25`""
} else {
    $script:convert_to_mp4_commandline += " -eo `"-c hevc --profile main10 --qvbr-quality 25 --gop-len 90 --cqp 20:23:25`""
}
$script:convert_to_mp4_commandline += " -fmt mp4"
$script:convert_to_mp4_commandline += " -m `"$script:amatsukaze_directory\exe_files\muxer.exe`""
$script:convert_to_mp4_commandline += " -bcm 0.5"
$script:convert_to_mp4_commandline += " --chapter"
$script:convert_to_mp4_commandline += " -f `"$script:amatsukaze_directory\avscache\17E37FEB.avs`""
$script:convert_to_mp4_commandline += " --subtitles"
$script:convert_to_mp4_commandline += " --jls-cmd `"$script:amatsukaze_directory\JL\JL_Standard.txt`""
if( $script:nvenc_flag ){
    $script:convert_to_mp4_commandline += " --mpeg2decoder CUVID"
    $script:convert_to_mp4_commandline += " --h264decoder CUVID"
} else {
    $script:convert_to_mp4_commandline += " --mpeg2decoder default"
    $script:convert_to_mp4_commandline += " --h264decoder default"
}
$script:convert_to_mp4_commandline += " --ignore-no-drcsmap"
$script:convert_to_mp4_commandline += " --no-delogo"
if( $ignore_no_logo_flag )
{
    $script:convert_to_mp4_commandline += " --ignore-no-logo"
}
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID171-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1032-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1040-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1040-2.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1048-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1056-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1064-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1072-1.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID1072-2.lgd`""
$script:convert_to_mp4_commandline += " --logo `"$script:amatsukaze_directory\logo\SID23608-1.lgd`""

[int]$delay_shutdown = 180

#########################
# Help
#########################
if( $help ) {
    Write-Host "NAME"
    Write-Host ""
    Write-Host "   $script:script_name - M2TSファイルをCMを抜いたMP4形式に変換する"
    Write-Host ""
    Write-Host "SYPNOSYS"
    Write-Host ""
    Write-Host "   $script:script_name -log_file <file path>"
    Write-Host "                       -source <directory path>"
    Write-Host "                       -destination <directory path>"
    Write-Host "                       [-nvenc] [-quiet] [-shutdown] [-help]"
    Write-Host ""
    Write-Host "DESCRIPTION"
    Write-Host ""
    Write-Host "   -source"
    Write-Host "        変換元のm2tsファイルが格納されているディレクトリを指定する"
    Write-Host ""
    Write-Host "   -destination"
    Write-Host "        mp4ファイルの格納先のディレクトリを指定する"
    Write-Host ""
    Write-Host "   -nvenc"
    Write-Host "        NVEncを使用してエンコードする。指定しない場合はQVEncを使用してエンコードする"
    Write-Host ""
    Write-Host "   -quiet"
    Write-Host "        処理開始前の確認をスキップする"
    Write-Host ""
    Write-Host "   -ignore_no_logo"
    Write-Host "        ロゴ無し番組のCMカット処理を許可する。"
    Write-Host "        このフラグを設定しない場合、ロゴがない番組はエラーで変換処理が止まる"
    Write-Host ""
    Write-Host "   -shutdown"
    Write-Host "        変換終了後、${delay_shutdown}秒後にPCをシャットダウンする"
    Write-Host ""
    Write-Host "   -help"
    Write-Host "        ヘルプを表示する"
    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
}

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 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("_") -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) )
        }
        $console_output += "`n  変換ファイル格納先:$destination"
        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

    [string]$temp_file = ${script:work_directory} + "\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

    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"))
    }
    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 CheckAlreadyRunning {
    [int]$number_of_process = (Get-Process -Name powershell | Where-Object -FilterScript {$_.Id -ne $PID}).Count

    return ($number_of_process -gt 0)
}

#########################
# Parameter check
#########################
if( [string]::IsNullOrEmpty("${script:source_root}") -or -Not(Test-Path "${script:source_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No source folder '"+${script:source_root}+"'")
    exit
}

if( [string]::IsNullOrEmpty("${script:destination_root}") -or -Not(Test-Path "${script:destination_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No destination folder '"+${script:destination_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 converting M2TS to MP4"
OutputLog -type Info -message "  変換元フォルダ:$script:source_root"
OutputLog -type Info -message "  変換先フォルダ:$script:destination_root"

if( CheckAlreadyRunning ){
    OutputLog -type Info -message "変換・コピー処理実行中のため中断します"
    exit
}

Remove-Item "${script:work_directory}/*" -Exclude .gitkeep -Recurse -Force > $null

if( -Not(Test-Path "${script:destination_root}") ){
    New-Item -Type Directory ${script:destination_root} > $null
}
ConvertToMp4 -source "$script:source_root" -destination "$script:destination_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 converting M2TS to MP4"
Param(
    [string]$log_file,
    [string]$source,
    [string]$destination,
    [switch]$quiet,
    [switch]$delete,
    [switch]$shutdown,
    [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 = $script:script_path+((Get-Date).ToString("\\yyyyMMdd_HHmmss_"))+$script_name.Replace(".ps1",".log")
}

[string]$script:source_root      = $source
[string]$script:destination_root = $destination

[bool]$script:quiet_flag=$quiet

[bool]$debug=$false
if($debug){
    [string]$script:source_root      = "${script:script_path}\files\convert"
    [string]$script:destination_root = "${script:script_path}\files\convert_backup"

    if( -Not(Test-Path "${script:destination_root}") ){
        New-Item -Type Directory ${script:destination_root} > $null
    }
}

[int]$delay_shutdown = 180

#########################
# Help
#########################
if( $help ) {
    Write-Host "NAME"
    Write-Host ""
    Write-Host "   $script:script_name - MP4ファイルをコピーする"
    Write-Host ""
    Write-Host "SYPNOSYS"
    Write-Host ""
    Write-Host "   $script:script_name -log_file <file path>"
    Write-Host "                       -source <directory path>"
    Write-Host "                       -destination <directory path>"
    Write-Host "                       [-quiet] [-help]"
    Write-Host ""
    Write-Host "DESCRIPTION"
    Write-Host ""
    Write-Host "   -source"
    Write-Host "        mp4ファイルが格納されているディレクトリを指定する"
    Write-Host ""
    Write-Host "   -destination"
    Write-Host "        mp4ファイルのコピー先のディレクトリを指定する"
    Write-Host ""
    Write-Host "   -quiet"
    Write-Host "        処理開始前の確認をスキップする"
    Write-Host ""
    Write-Host "   -destination"
    Write-Host "        sourceにないファイルを削除する。quietオプションは無視される"
    Write-Host ""
    Write-Host "   -shutdown"
    Write-Host "        コピー終了後、${delay_shutdown}秒後にPCをシャットダウンする"
    Write-Host ""
    Write-Host "   -help"
    Write-Host "        ヘルプを表示する"
    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
}

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 CopyMp4 {
    Param(
        [string]$source,
        [string]$destination
    )

    OutputLog -type Info -function "CopyMp4" -message "Start copying MP4"

    $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("_") -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)
            {
                $copy_files += $mp4_file
            }
        }
    }

    if( $copy_files.Count -gt 0 )
    {
        [string]$console_output = "以下のファイルをコピーします。"
        foreach( $copy_file in $copy_files ){
            $console_output += ( "`n  - ${copy_file}" )
        }
        $console_output += "`n  コピー先:$destination"
        OutputLog -type Info -function "CopyMp4" -message $console_output

        if( !$script:quiet_flag )
        {
            $response = ( Read-Host "よろしいですか?([Y]es/[n]o" )
            switch($response)
            {
                "" {}
                "y" {}
                default { return; }
            }
        }
    }

    foreach( $copy_file in $copy_files )
    {
        CopyMp4_OneFile -source $copy_file.FullName -destination $copy_file.FullName.Replace($source,$destination)
    }

    OutputLog -type Info -function "CopyMp4" -message "Finish copying MP4"
}

function CopyMp4_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 "CopyMp4_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 "CopyMp4_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 "CopyMp4_OneFile" -message ( "Copy duration : " + (GetDurationString($duration))  )

    [string]$destination_file_path = ConvertPath($destination)
    if(-Not(Test-Path($destination_file_path))){
        OutputLog -type Error -function "CopyMp4_OneFile" -message ( "Fail to copy : " + $source_file_name  )
    }
}
function DeleteFiles {
    Param(
        [string]$source,
        [string]$destination
    )

    OutputLog -type Info -function "DeleteFiles" -message "Start deleting files from destination 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
        } 
    }
}

function CheckAlreadyRunning {
    [int]$number_of_process = (Get-Process -Name powershell | Where-Object -FilterScript {$_.Id -ne $PID}).Count

    return ($number_of_process -gt 0)
}

#########################
# Parameter check
#########################
if( [string]::IsNullOrEmpty("${script:source_root}") -or -Not(Test-Path "${script:source_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No source folder '"+${script:source_root}+"'")
    exit
}

if( [string]::IsNullOrEmpty("${script:destination_root}") -or -Not(Test-Path "${script:destination_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No destination folder '"+${script:destination_root}+"'")
    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 copying MP4"
OutputLog -type Info -message "  コピー元フォルダ:$script:source_root"
OutputLog -type Info -message "  コピー先フォルダ:$script:destination_root"

if( CheckAlreadyRunning ){
    OutputLog -type Info -message "変換・コピー処理実行中のため中断します"
    exit
}

# Copy MP4
CopyMp4 -source "$script:source_root" -destination "$script:destination_root"

# delete
if( $delete ){
    DeleteFiles -source "$script:source_root" -destination "$script:destination_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 copying MP4"
Param(
    [string]$log_file,
    [string]$source,
    [int]$day,
    [switch]$quiet,
    [switch]$shutdown,
    [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 = $script:script_path+((Get-Date).ToString("\\yyyyMMdd_HHmmss_"))+$script_name.Replace(".ps1",".log")
}

[string]$script:source_root      = $source
[string]$script:duration_day     = $day

[bool]$script:quiet_flag=$quiet

[bool]$debug=$false
if($debug){
    [string]$script:source_root = "${script:script_path}\files\convert_backup"
    [int]$script:duration_day   = 1
}

#########################
# Help
#########################
if( $help ) {
    Write-Host "NAME"
    Write-Host ""
    Write-Host "   $script:script_name - 最終更新日から指定した日数を経過したファイルを削除する"
    Write-Host ""
    Write-Host "SYPNOSYS"
    Write-Host ""
    Write-Host "   $script:script_name -log_file <file path>"
    Write-Host "                       -source <directory path>"
    Write-Host "                       -day <number of day>"
    Write-Host "                       [-quiet] [-help]"
    Write-Host ""
    Write-Host "DESCRIPTION"
    Write-Host ""
    Write-Host "   -source"
    Write-Host "        ファイル削除対象のディレクトリを指定する"
    Write-Host ""
    Write-Host "   -quiet"
    Write-Host "        処理開始前の確認をスキップします"
    Write-Host ""
    Write-Host "   -shutdown"
    Write-Host "        コピー終了後、${delay_shutdown}秒後にPCをシャットダウンする"
    Write-Host ""
    Write-Host "   -help"
    Write-Host "        ヘルプを表示する"
    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
}

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 DeleteFiles {
    Param(
        [string]$source,
        [int]$day
    )

    OutputLog -type Info -function "DeleteFiles" -message "Start deleting files from source folder"

    $files = Get-ChildItem "$source" -Recurse

    [string[]]$delete_files = @()
    foreach( $file in $files ){

        if( !$file.Name.StartsWith("_") -and !$file.Name.StartsWith(".") ){
            [timespan]$time_diff = [DateTime]::Now - $file.LastWriteTime
            if( $time_diff.TotalDays -gt $day ){
                $delete_files += $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" )
        }
        OutputLog -type Info -function "DeleteFiles" -message $console_output

        if( !$script:quiet_flag )
        {
            $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))
    }
}

function DeleteFolders {
    Param(
        [string]$source
    )

    $folders = (Get-ChildItem $source -Directory -Recurse -ErrorAction Stop) | Sort-Object -Descending

    $delete_folders = @()
    foreach($folder in $folders){
        if ($folder.GetFileSystemInfos().Count -eq 0) {
            $delete_folders += $folder
        }
    }

    if( $delete_folders.Count -gt 0 )
    {
        [string]$console_output = "以下の空フォルダを削除します。`n"
        foreach( $delete_folder in $delete_folders )
        {
            $console_output += ( "  - " + (Split-Path -Leaf $delete_folder) + "`n" )
        }
        OutputLog -type Info -function "DeleteFolders" -message $console_output

        if( !$script:quiet_flag )
        {
            $console_output = "よろしいですか?([y]es/[N]o)"

            $response = ( Read-Host $console_output ).Trim()
            switch($response)
            {
                "y" {}
                default { return; }
            }
        }
    }

    foreach($delete_folder in $delete_folders){
        Remove-Item  $delete_folder.FullName -Recurse -Force
    }
}

function CheckAlreadyRunning {
    [int]$number_of_process = (Get-Process -Name powershell | Where-Object -FilterScript {$_.Id -ne $PID}).Count

    return ($number_of_process -gt 0)
}

#########################
# Parameter check
#########################
if( [string]::IsNullOrEmpty("${script:source_root}") -or -Not(Test-Path "${script:source_root}") ){
    OutputLog -type Error -function "ParameterCheck" -message ( "No source folder '"+${script:source_root}+"'")
    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 deleting file"
OutputLog -type Info -message "  ファイル削除対象フォルダ:$script:source_root"
OutputLog -type Info -message "  (最終書込日時から${script:duration_day}日経過したファイルを削除します)"

if( CheckAlreadyRunning ){
    OutputLog -type Info -message "変換・コピー処理実行中のため中断します"
    exit
}

# delete
DeleteFiles -source "$script:source_root" -day ${script:duration_day}
DeleteFolders -source "$script:source_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 deleting file"