以下の内容はhttps://nextaltair.hatenablog.com/entry/edcb_auto_encode_batch_file_utf8_problem_solution_with_ps1より取得しました。


EDBCでバッチファイルを使って自動でエンコードさせると文字化けする問題をps1を使って解決

Windows 11 をクリーンインストールしたついでに、EDCB の録画後エンコード環境を見直した。bat ファイルで自動エンコードやろうとしてた時の文字化け問題、あれを PowerShell + HandBrakeCLI に乗り換えて解決した

0. やったことの流れ

  1. EDCB 0.10.70.0 (tkntrec-241220) へアップデート。
  2. 設定ファイル(EDCB\Setting.txt) の文字エンコーディングを UTF-8 BOM に統一。
  3. 録画後の処理を、バッチファイル (.bat) から PowerShell スクリプト (.ps1) に変更。
  4. RecName_Macro.dll の設定をして、.ts ファイルのパスの長さを切り詰めておく。
  5. HandBrake 用のプリセット ( .json ) を作った。*1 6.Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine を管理者ターミナルから実行
  6. 作成した autoencode.ps1 を EDCB に登録して完了。

1. 文字化け問題と PowerShell への移行理由

  • EDCB の録画ダイアログにある 録画終了後実行bat のことをググってもパープレにきいてもバッチはSift JISでとあるが、情報が古い。
  • 設定ファイルの中にある .txt を UTF-8 BOM にしても、バッチが実行さる前に作成される EpgTimer_Bon_RecEnd.batに記述されるパスは文字化けしてる。
  • bat が UTF-8 で動かない理由: cmd.exe は既定で コードページ 932 (Shift_JIS) でバッチを解釈。UTF‑8 で保存しても、EDCB から cmd /c で呼ばれる時点で 932 に戻るため日本語が ?? 化する。chcp 65001 を先頭に置いても処理途中で 932 に戻るため根本解決にならない。
  • 「バッチ捨てて PS で処理させれば  UTF-8 が扱えて文字化けを解決できる」 という結論。

2. PowerShell スクリプト (autoencode.ps1)

Gemini 2.5 Pro に書いてもらった。

# _EDCBX_HIDE_
#Requires -Version 5.0
<#
.SYNOPSIS
EDCB録画後処理スクリプト (HandBrakeによるエンコード)

.DESCRIPTION
EDCBの録画完了後に呼び出され、指定されたTSファイルをHandBrakeCLIでエンコードします。
EDCBからは環境変数経由でファイルパス($env:FilePath)や録画タグ($env:BatFileTag)などを受け取ります。
設定はスクリプト内の変数と、同ディレクトリにある HandBrake プリセット JSON ファイルで行います。
ログは指定されたディレクトリに出力されます。

EDCB拡張命令について:
- 先頭の `# _EDCBX_HIDE_` は、スクリプト実行時のコンソールウィンドウを非表示にするためのEDCB拡張命令です。
  詳細は [@docs/edcb_post_rec_script_spec.md](mdc:docs/edcb_post_rec_script_spec.md) を参照してください。
- PowerShell スクリプトの場合、EDCB拡張命令の `_EDCBX_DIRECT_` (マクロの環境変数渡し) と
  `_EDCBX_FORMATTIME_` (日付/時刻マクロのISO8601形式化) は常に有効として扱われます。
  このスクリプトでは $env:FilePath を直接利用しています。
#>

param(
    # CLIからのテスト実行用に、引数でファイルパスを受け取る
    [Parameter(Mandatory=$false, Position=0)] # Mandatory=$false にしてEDCB実行時にもエラーにならないように
    [string]$CliInputPath
)

# --- 設定 --- Start ---

# HandBrakeCLI.exe のフルパス
$handbrakeCliPath = "C:\TVRec\EDCB\HandBrakeCLI.exe"

# エンコード済みファイルの出力先ディレクトリ
$outputDir = "D:\Encoded"

# ログファイルの出力先ディレクトリ
$logDir = "C:\TVRec\EDCB\autoencode\Log"

# HandBrakeプリセットファイル名 (スクリプトと同じディレクトリにある前提)
$presetFile = "EncodePreset01.json"

# 使用するプリセット名 (JSONファイル内の "PresetName")
$presetName = "MovieDramaAnime"

# エラーファイルを移動するディレクトリ
$errorDir = "J:\TVFile\EncodeError"

# --- 設定 --- End ----

# --- メイン処理 --- Start ---

# 文字コードをUTF-8に設定 (スクリプトとログ出力のため)
$OutputEncoding = [System.Text.Encoding]::UTF8
[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# ログファイルパスの生成
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss.ff"
$logFileName = "encode_$timestamp.log"
$logFilePath = Join-Path -Path $logDir -ChildPath $logFileName

# ログディレクトリ作成
if (-not (Test-Path -Path $logDir -PathType Container)) {
    try {
        New-Item -Path $logDir -ItemType Directory -ErrorAction Stop | Out-Null
    } catch {
        Write-Error "ログディレクトリの作成に失敗しました: $logDir $($_.Exception.Message)"
        exit 1 # ディレクトリ作成失敗は致命的
    }
}

# ログ出力関数
function Write-Log {
    param(
        [Parameter(Mandatory=$true)]
        [string]$Message
    )
    $logTimestamp = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
    "$logTimestamp $Message" | Out-File -FilePath $logFilePath -Append -Encoding utf8
}

Write-Log "==== バッチファイル開始 ===="

# 入力ファイルの取得
$inputFile = $null
if (-not [string]::IsNullOrEmpty($CliInputPath)) {
    # CLIから引数が渡された場合 (テスト実行)
    $inputFile = $CliInputPath
    Write-Log "入力ファイル (CLI引数): $inputFile"
} else {
    # EDCBからの実行の場合 (環境変数)
    $inputFile = $env:FilePath
    if ([string]::IsNullOrEmpty($inputFile)) {
        Write-Log "エラー: 入力ファイルパスが環境変数から取得できませんでした。"
        exit 1
    }
    Write-Log "入力ファイル (環境変数): $inputFile"
}

# 録画タグのログ出力
if (-not [string]::IsNullOrEmpty($env:BatFileTag)) {
    Write-Log "録画タグ (BatFileTag): $($env:BatFileTag)"
} else {
    Write-Log "録画タグ (BatFileTag): (設定なし)"
}

# 入力ファイルの存在確認 (念のため)
if (-not (Test-Path -LiteralPath $inputFile -PathType Leaf)) {
    Write-Log "エラー: 指定された入力ファイルが存在しません: $inputFile"
    exit 1
}

# HandBrakeCLIの存在確認
if (-not (Test-Path -Path $handbrakeCliPath -PathType Leaf)) {
    Write-Log "エラー: HandBrakeCLIが見つかりません: $handbrakeCliPath"
    exit 1
}

# 出力ディレクトリ作成 (存在しない場合)
if (-not (Test-Path -Path $outputDir -PathType Container)) {
    try {
        New-Item -Path $outputDir -ItemType Directory -ErrorAction Stop | Out-Null
        Write-Log "出力ディレクトリを作成しました: $outputDir"
    } catch {
        Write-Log "エラー: 出力ディレクトリの作成に失敗しました: $outputDir $($_.Exception.Message)"
        exit 1
    }
}

# 出力ファイル名の生成
$inputBaseName = [System.IO.Path]::GetFileNameWithoutExtension($inputFile)
$outputFileName = "$inputBaseName.mp4"
$outputFilePath = Join-Path -Path $outputDir -ChildPath $outputFileName

Write-Log "出力ファイル: $outputFilePath"

# プリセットファイルのフルパス生成
$presetFilePath = Join-Path -Path $PSScriptRoot -ChildPath $presetFile
if (-not (Test-Path -Path $presetFilePath -PathType Leaf)) {
    Write-Log "エラー: HandBrakeプリセットファイルが見つかりません: $presetFilePath"
    exit 1
}

# HandBrakeCLIの引数リストを作成
$arguments = @(
    "-i", "`"$inputFile`"",
    "-o", "`"$outputFilePath`"",
    "--preset-import-file", "`"$presetFilePath`"",
    "--preset", "`"$presetName`""
)

Write-Log "エンコード開始: $(Get-Date -Format 'yyyy/MM/dd HH:mm:ss')"
Write-Log "コマンド: `"$handbrakeCliPath`" $($arguments -join ' ')"

# HandBrakeCLIの実行とログリダイレクト
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
$processInfo.FileName = $handbrakeCliPath
$processInfo.Arguments = $arguments -join ' '
$processInfo.RedirectStandardOutput = $true
$processInfo.RedirectStandardError = $true
$processInfo.UseShellExecute = $false
$processInfo.CreateNoWindow = $true # コンソールウィンドウを表示しない
$processInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$processInfo.StandardErrorEncoding = [System.Text.Encoding]::UTF8

$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processInfo

# 標準出力と標準エラー出力をログファイルに追記するイベントハンドラ
$process.EnableRaisingEvents = $true
$stdOutEvent = Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -Action {
    if (-not [string]::IsNullOrEmpty($EventArgs.Data)) {
        # HandBrakeの進捗表示(行頭が)はログに出さないようにする (任意)
        if ($EventArgs.Data -notmatch '^\r') {
             ("$($EventArgs.Data)") | Out-File -FilePath $script:logFilePath -Append -Encoding utf8
        }
    }
} -SourceIdentifier StdOutHandler
$stdErrEvent = Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -Action {
    if (-not [string]::IsNullOrEmpty($EventArgs.Data)) {
        ("ERROR: $($EventArgs.Data)") | Out-File -FilePath $script:logFilePath -Append -Encoding utf8
    }
} -SourceIdentifier StdErrHandler

$process.Start() | Out-Null
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()

$process.WaitForExit()

# イベントハンドラの解除
Unregister-Event -SourceIdentifier StdOutHandler
Unregister-Event -SourceIdentifier StdErrHandler
$stdOutEvent | Remove-Job
$stdErrEvent | Remove-Job

$exitCode = $process.ExitCode

if ($exitCode -eq 0) {
    # HandBrake 正常終了
    Write-Log "HandBrake 正常終了 (終了コード: 0)"

    # 1. 出力ファイルの存在確認
    if (Test-Path -LiteralPath $outputFilePath -PathType Leaf) {
        Write-Log "出力ファイルが存在します: $outputFilePath"

        # 2. 出力ファイルのサイズ確認 (例: 1MB以上)
        $outputFileSize = $null
        try {
            $outputFileSize = (Get-Item -LiteralPath $outputFilePath -ErrorAction Stop).Length
        } catch {
             Write-Log "エラー: 出力ファイルの情報取得に失敗しました: $($_.Exception.Message)"
             exit 1 # ファイル情報が取れないのは異常
        }
      
        if ($outputFileSize -gt 1MB) { # 1MB = 1048576 bytes
            Write-Log "出力ファイルのサイズが妥当です ($([Math]::Round($outputFileSize / 1MB, 2)) MB)"

            # --- ここまでのチェックをクリア ---
            Write-Log "エンコード成功と判断しました。"
            Write-Log "元のファイルを削除します: $inputFile"
            try {
                Remove-Item -LiteralPath $inputFile -Force -ErrorAction Stop
                Write-Log "元のファイルを削除しました。"
                # 正常終了
                exit 0
            } catch {
                Write-Log "エラー: 元ファイルの削除に失敗しました: $($_.Exception.Message)"
                exit 1 # 削除失敗はエラー扱い
            }

        } else {
            Write-Log "エラー: 出力ファイルのサイズが小さすぎます ($outputFileSize バイト)。エンコード失敗と判断します。"
            # 失敗したので、念のため生成された(かもしれない)小さい出力ファイルも削除する (任意)
            try {
                Remove-Item -LiteralPath $outputFilePath -Force -ErrorAction SilentlyContinue # エラーは無視
                Write-Log "サイズの小さい出力ファイルを削除しました: $outputFilePath"
            } catch {}
            # 元ファイルをエラーフォルダへ移動
            Move-ToErrorFolder $inputFile
            exit 1
        }
    } else {
        Write-Log "エラー: 出力ファイルが見つかりません。エンコード失敗と判断します。"
        # 元ファイルをエラーフォルダへ移動
        Move-ToErrorFolder $inputFile
        exit 1
    }
} else {
    # HandBrake 異常終了
    Write-Log "エラー: HandBrake が異常終了しました (終了コード: $exitCode)"
    # 元ファイルをエラーフォルダへ移動
    Move-ToErrorFolder $inputFile
    exit 1
}

# --- 関数定義: エラーフォルダへの移動 ---
function Move-ToErrorFolder {
    param(
        [Parameter(Mandatory=$true)]
        [string]$SourcePath
    )
    # エラーディレクトリ作成 (存在しない場合)
    if (-not (Test-Path -LiteralPath $script:errorDir -PathType Container)) { # スクリプトスコープの変数を参照
        try {
            New-Item -Path $script:errorDir -ItemType Directory -ErrorAction Stop | Out-Null
            Write-Log "エラーファイル移動先ディレクトリを作成しました: $script:errorDir"
        } catch {
            Write-Log "エラー: エラーファイル移動先ディレクトリの作成に失敗しました: $script:errorDir $($_.Exception.Message)"
            # ディレクトリ作成失敗時は移動を試みない
            return
        }
    }
  
    $destinationPath = Join-Path -Path $script:errorDir -ChildPath ([System.IO.Path]::GetFileName($SourcePath))
    Write-Log "元のファイルをエラーフォルダへ移動します: $destinationPath"
    try {
        Move-Item -LiteralPath $SourcePath -Destination $destinationPath -Force -ErrorAction Stop
        Write-Log "元のファイルをエラーフォルダへ移動しました。"
    } catch {
        Write-Log "エラー: 元ファイルの移動に失敗しました: $($_.Exception.Message)"
    }
}

# --- メイン処理 --- End ----

スクリプトのポイント

  • EDCB連携:
    • # _EDCBX_HIDE_ コンソールウィンドウを非表示にする。
    • 録画タグ ($env:BatFileTag) はログに出す。現時点ではログに記録するだけ使い道はまた今度。
  • HandBrakeCLI実行:
    • System.Diagnostics.Process を仕様して HandBrakeCLI を直接動かす。
    • HandBrake の出すメッセージ(標準出力・エラー出力)を横取りして、リアルタイムでログファイルに書き込んでる。これで進捗、エラー内容が追いやすい。
  • エラーハンドリング:
    • エンコードが終わったら、念のため出来上がったファイルがあるか、サイズが小さすぎないか見てる。ダメそうなら失敗扱い。
    • 失敗したら、元の TS ファイルはエラー用フォルダ ($errorDir) に移動させる (Move-ToErrorFolder って関数)。
  • 設定の外部化:
    • エンコード設定は外の JSON ファイル ($presetFile, $presetName) で管理。 設定変える時にスクリプト変更がいらない
    • 使うツールのパスとか、出力先フォルダとかは、スクリプトの最初のほうで変数 ($handbrakeCliPath とか) に記載。
  • ログ出力:
    • スクリプト自体の動きは Write-Log って関数でログに書出し
    • HandBrake の細かい出力は、ログに記録。

3. EDCB への登録

  1. EpgTimer (または EpgTimerSrv設定ツール) を起動。
  2. 録画ダイヤログか、 設定> 動作設定 > 全般の録画プリセット設定を開く。
  3. 録画終了後実行batファイル の欄に、作成した autoencode.ps1 のフルパス(例: C:\TVRec\EDCB\autoencode\autoencode.ps1)を入力する。
  4. 引数は不要(スクリプトが環境変数からファイルパスを取得するため)。

これで、録画が終わった後この autoencode.ps1 が動いてエンコードが始まるはず。


4. 最後

  • bat ファイルで苦しんでた Shift_JIS 由来の文字化け問題は、EDCBのバージョンアップと EDCB/Setting ファイルの文字エンコーディングを UTF-8 BOM に統一と、実行ファイルの PowerShell に変えたら解決した。
  • エンコード設定を JSON ファイルで外出しにしたから、後々のメンテが楽になったはず。

5. 参考

EDCB+HandBrake でTSからMP4へ自動コンバートバッチ|はぐ

GitHub - tsukumijima/DTV-Builds: TS抜き(DTV)関連ソフトウェアのビルド済みアーカイブ

EDCB/Document/Readme_Mod.txt at work-plus-s · xtne6f/EDCB · GitHub

*1:今回の話とは関係ないんで解説はしない




以上の内容はhttps://nextaltair.hatenablog.com/entry/edcb_auto_encode_batch_file_utf8_problem_solution_with_ps1より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14