テクノロジー · 実用ガイド

エンコードせずに HandBrake のキューの字幕フラグを確認する

保存済みキューの表で検索要求と明示的な字幕トラックを分け、true、false、未記録の違いを保ちます。

保存した字幕検索要求と明示トラックを分けるオリジナル図解
仮定の例によるオリジナルイラスト。検索のみのジョブは出力トラックを確認しません。埋め込み Track 0 と標準インポート Track -1 は別の例です。 Watch at Ease

一括処理キューには、明示的な字幕トラックがなく、外国語音声の字幕検索要求だけが入っている場合があります。両者を同じものと見なすと確認を誤ります。スキャンの要求は、どの字幕が選ばれ、出力に現れるかの確認ではありません。

HandBrake は保存したプリセットの字幕選択動作と、キューの個別編集を既に提供しています。このガイドは、自分の対応するエクスポートのコピーから、選んだ設定を限定的にオフライン CSV で確認する方法です。ソフトの不具合を直すものではなく、キュー変更、メディアの読み込み、エンコード開始もしません。

適切な保存済みエクスポートを用意する

一括追加の前に、プリセットで字幕選択動作を設定して保存してください。保存されるのは選択動作であり、手動で選んだ固定トラック一覧ではありません。追加済みジョブには標準の Edit を使い、修正したジョブを再追加します。各ジョブを個別に確認することも有効な標準機能です。

この手順では、対応する Windows キューの CLI 用エクスポートの別コピーを queue.json として保管します。現在の Windows ソースは、Source、Destination、Subtitle を持つ Job を含むオブジェクトの配列を作ります。通常の GUI キューバックアップは異なる構造で、拒否されます。CLI 用エクスポートは完了したジョブを除外しますが、この表は状態を示さず、全ジョブが待機中とも主張しません。

これは保存時点のスナップショットで、ライブ監視ではありません。UTF-8 JSON、配列の SubtitleList、選んだフィールドの対応する型が必要です。Search がない場合は未記録として受け入れますが、明示的な null の Search、古い SRT 構造、未対応の構造は拒否します。他のプラットフォームの GUI エクスポートや将来の形式は対象外です。

既存ファイルを上書きせず表を作る

Python 3 が使える Linux Bash 端末で queue.json のコピーがあるフォルダーを開きます。次のコマンドを一行としてコピーし、直線の引用符とバックスラッシュを保ってください。既存の subtitles.csv は上書きせず拒否します。この正確なコマンドを実行したのは Linux Bash のみで、Windows と macOS では実行していません。

変換器は表の全行を解析・検証した後に CSV を作ります。JSON キーの重複、非標準の数値、選んだフィールドの不正な型、対応する構造の不正な形を拒否します。元メディアもインポート字幕ファイルも開きません。未知の設定を監査するものではなく、選んだ十七フィールドの表であり、エンコード設定全体の検証ではありません。

python3 -c 'exec("import csv, json, sys\nfrom pathlib import Path\ndef pairs(items):\n d = {}\n for k, v in items:\n if k in d:\n raise ValueError('"'"'Duplicate JSON key'"'"')\n d[k] = v\n return d\ndef bad(value):\n raise ValueError('"'"'Nonstandard JSON number'"'"')\ndef obj(v):\n if not isinstance(v, dict):\n raise ValueError('"'"'Expected object'"'"')\n return v\ndef text(v):\n if v is None: return '"'"''"'"'\n if not isinstance(v, str): raise ValueError('"'"'Expected string'"'"')\n v.encode('"'"'utf-8'"'"')\n return v\ndef integer(v):\n if v is None: return '"'"''"'"'\n if type(v) is not int: raise ValueError('"'"'Expected integer'"'"')\n return str(v)\ndef flag(v):\n if v is None: return '"'"''"'"'\n if type(v) is not bool: raise ValueError('"'"'Expected Boolean'"'"')\n return '"'"'true'"'"' if v else '"'"'false'"'"'\nsrc, dst = map(Path, sys.argv[1:])\njobs = json.loads(src.read_bytes().decode('"'"'utf-8-sig'"'"'), object_pairs_hook=pairs, parse_constant=bad)\nif not isinstance(jobs, list): raise ValueError('"'"'Expected CLI queue array'"'"')\nrows = []\nfor n, wrapper in enumerate(jobs, 1):\n job = obj(obj(wrapper)['"'"'Job'"'"'])\n source, dest, sub = obj(job['"'"'Source'"'"']), obj(job['"'"'Destination'"'"']), obj(job['"'"'Subtitle'"'"'])\n title = source['"'"'Title'"'"']\n if type(title) is not int or title < 1: raise ValueError('"'"'Expected positive title index'"'"')\n base = [str(n), text(source['"'"'Path'"'"']), text(dest['"'"'File'"'"']), str(title)]\n search = obj(sub.get('"'"'Search'"'"', {}))\n rows.append(base + ['"'"'search'"'"', '"'"''"'"', '"'"''"'"', flag(search.get('"'"'Enable'"'"'))] + [flag(search.get(k)) for k in ['"'"'Burn'"'"', '"'"'Default'"'"', '"'"'Forced'"'"']] + ['"'"''"'"'] * 6)\n tracks = sub['"'"'SubtitleList'"'"']\n if not isinstance(tracks, list): raise ValueError('"'"'Expected subtitle list'"'"')\n for i, item in enumerate(tracks, 1):\n item = obj(item)\n if '"'"'SRT'"'"' in item: raise ValueError('"'"'Legacy SRT shape unsupported'"'"')\n imp = item.get('"'"'Import'"'"')\n if imp is None:\n track = item['"'"'Track'"'"']\n if type(track) is not int or track < 0: raise ValueError('"'"'Expected embedded track index'"'"')\n kind, imp = '"'"'embedded'"'"', {}\n else:\n kind, imp = '"'"'import'"'"', obj(imp)\n if not text(imp['"'"'Filename'"'"']): raise ValueError('"'"'Import filename missing'"'"')\n rows.append(base + [kind, str(i), integer(item.get('"'"'Track'"'"')), '"'"''"'"'] + [flag(item.get(k)) for k in ['"'"'Burn'"'"', '"'"'Default'"'"', '"'"'Forced'"'"']] + [integer(item.get('"'"'Offset'"'"'))] + [text(imp.get(k)) for k in ['"'"'Filename'"'"', '"'"'Language'"'"', '"'"'Codeset'"'"', '"'"'Format'"'"']] + [text(item.get('"'"'Name'"'"'))])\nheader = ['"'"'job'"'"', '"'"'source'"'"', '"'"'destination'"'"', '"'"'title'"'"', '"'"'row_kind'"'"', '"'"'entry'"'"', '"'"'track'"'"', '"'"'enable'"'"', '"'"'burn'"'"', '"'"'default'"'"', '"'"'forced'"'"', '"'"'offset_ms'"'"', '"'"'import_file'"'"', '"'"'import_language'"'"', '"'"'import_codeset'"'"', '"'"'import_format'"'"', '"'"'name'"'"']\nwith dst.open('"'"'x'"'"', encoding='"'"'utf-8-sig'"'"', newline='"'"''"'"') as f:\n w = csv.writer(f); w.writerow(header); w.writerows(rows)\nwith dst.open(encoding='"'"'utf-8-sig'"'"', newline='"'"''"'"') as f:\n if list(csv.reader(f)) != [header] + rows: raise ValueError('"'"'Readback mismatch; discard partial CSV'"'"')\nprint(len(jobs), '"'"'saved jobs and'"'"', len(rows), '"'"'report rows checked; not an encode result'"'"')\n")' "queue.json" "subtitles.csv"

検索行と明示的なトラック行を分けて読む

列は job、source、destination、title、row_kind、entry、track、enable、burn、default、forced、offset_ms、import_file、import_language、import_codeset、import_format、name です。ジョブと明示的な項目の番号は保存順に 1 から始まり、重複ジョブも残ります。元パス、出力パス、名前は解析された文字列で、パス展開や存在確認はしません。

各ジョブには Search フラグを記録する row_kind が search の行が一つあり、その後に SubtitleList の明示的な項目の行が続きます。enable の true は検索要求で、確認済みの字幕選択ではありません。検索を有効にして明示リストが空の状態と、検索を設定していない状態は異なります。真偽値は true または false、選んだフィールドの欠落や null は空欄で、未記録を意味し、false と推測しません。トラック行の enable は検索用フィールドなので空欄です。

埋め込み Track はゼロ起点で、0 は有効な最初のトラックです。標準のインポート項目は Track -1 と独立した Import オブジェクトを使います。元のファイル名、言語、文字コード、形式を記録しますが、字幕ファイルは調べません。Offset は負数も含むミリ秒の整数で、秒ではありません。フラグやオフセットだけではファイルの存在、想定言語との一致、出力コンテナーでの正常再生は確認できません。

一括処理を承認する前に照合する

コマンドはジョブ数と表の行数を表示する前に CSV を再読込し、ヘッダーと全フィールドを比較します。各エクスポート済みジョブに検索行がちょうど一つあることを確認し、既知の埋め込み・インポート項目を標準キューと照合してください。CSV レコードを数え、テキストの物理行は数えません。引用されたフィールドには改行が入ることがあります。空のエクスポートはヘッダーのみです。元 JSON と CSV マスターを保管し、編集・絞り込みは作業コピーだけで行います。

LibreOffice Calc では UTF-8、カンマ区切り、直線の二重引用符を文字列区切りとして読み込みます。十七列をすべてテキストにし、区切りの結合、空白の削除、数式の評価を無効にします。画面が出なければフィルター設定の編集を有効にします。Calc の画面は実行していません。変更は HandBrake で行い、新しいスナップショットをエクスポートして別名の表を作ります。CSV 編集はキューを更新せず、この表はインポート用ファイルでもありません。

正確なコマンドで 26 件の合成テストを実行しました。正常七件は空リスト、明示トラックなしの検索、Track 0、インポート Track -1、負のオフセット、欠落と null、Unicode、引用符、改行、重複ジョブ順を含みます。不正十九件は CSV 作成前に停止しました。さらに上書き七回を拒否し、入力と既存出力のバイトは不変でした。実際の HandBrake GUI、キューエクスポート、エンコードは未検証です。書き込みや再読込に失敗したら部分 CSV を破棄して別名で再試行します。夜間処理に頼る前に、権利のある短い素材の試験エンコードと再生を別途確認してください。この表はスキャン結果や出力字幕を証明しません。

true、false、未記録のフィールド値を区別するオリジナル図解
オリジナルイラスト。選んだ値の欠落と null は共に空欄で、false ではありません。トラック行の検索 enable フィールドも空欄です。 Watch at Ease