# 帳票設計書 3-AviSynthスクリプト

## 概要

本ドキュメントは、StaxRipにおけるAviSynthスクリプト出力機能の設計書である。ビデオエンコード用のAviSynthフィルタスクリプトを生成し、.avsファイルとして出力する機能について記述する。

### 本帳票の処理概要

AviSynthスクリプト機能は、ユーザーが設定したビデオフィルタチェーン（ソースフィルタ、クロップ、リサイズ、デインターレース、ノイズ除去等）をAviSynthスクリプト言語で記述し、.avsファイルとして出力する機能である。

**業務上の目的・背景**：AviSynthは強力なフレームサーバーであり、ビデオエンコード時の前処理（フィルタリング）を記述するための標準的なツールである。StaxRipはユーザーフレンドリーなGUIを提供しつつ、バックエンドでAviSynthスクリプトを自動生成することで、高度なビデオ処理を可能にする。

**帳票の利用シーン**：エンコード処理開始時、プレビュー表示時、クロップ調整時、フィルタ設定変更時。

**主要な出力内容**：
1. プラグインロード命令（LoadPlugin）
2. AVSIインポート命令（Import）
3. ソースフィルタ（FFVideoSource、LWLibavVideoSource等）
4. クロップフィルタ（Crop）
5. リサイズフィルタ（BicubicResize、Spline64等）
6. デインターレースフィルタ（QTGMC等）
7. ノイズ除去フィルタ（DFTTest等）
8. ユーザー定義コード（CodeAtTop、CodeAtBottom）

**帳票の出力タイミング**：VideoScript.Synchronizeメソッド呼び出し時に自動生成される。

**帳票の利用者**：動画編集者、エンコード担当者、AviSynthスクリプト開発者。

## 帳票種別

スクリプト出力（AviSynthスクリプト）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | MainForm | メインフォーム | フィルタ設定変更時に自動生成 |
| - | CodeEditor | コードエディタ | スクリプト編集・保存時 |
| 2 | PreviewForm | プレビューフォーム | プレビュー表示時 |
| - | CropForm | クロップ調整フォーム | クロップ値変更時 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | AviSynthスクリプト (.avs) |
| 用紙サイズ | - |
| 向き | - |
| ファイル名 | {TargetFile.Base}.avs |
| 出力方法 | ファイル保存 |
| 文字コード | TextEncoding.EncodingOfProcess（システム依存） |

### スクリプト固有設定

| 項目 | 内容 |
|-----|------|
| 改行コード | システム依存（BR定数） |
| インデント | なし |
| コメント形式 | # コメント |

## 帳票レイアウト

### レイアウト概要

AviSynthスクリプトは、プラグインロード部、ソースフィルタ部、フィルタチェーン部で構成される。

```
┌─────────────────────────────────────┐
│  AddAutoloadDir(...)                │
│  LoadPlugin("plugin1.dll")          │
│  LoadPlugin("plugin2.dll")          │
│  Import("script.avsi")              │
├─────────────────────────────────────┤
│  # CodeAtTop                        │
│  {ユーザー定義コード}               │
├─────────────────────────────────────┤
│  # Source Filter                    │
│  FFVideoSource("source.mkv")        │
├─────────────────────────────────────┤
│  # Filter Chain                     │
│  Crop(...)                          │
│  BicubicResize(...)                 │
│  QTGMC(...)                         │
│  DFTTest(...)                       │
├─────────────────────────────────────┤
│  # CodeAtBottom                     │
│  {ユーザー定義コード}               │
└─────────────────────────────────────┘
```

### ヘッダー部（プラグインロード）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | AddAutoloadDir | オートロードディレクトリ追加 | FrameServerHelp.IsPortable | AddAutoloadDir("{path}") |
| 2 | LoadPlugin | DLLプラグインロード | Package.Items / AvsFilterNames | LoadPlugin("{path}") |
| 3 | Import | AVSIスクリプトインポート | Package.Items / AvsFilterNames | Import("{path}") |

### 明細部（フィルタチェーン）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | CodeAtTop | ユーザー定義先頭コード | p.CodeAtTop | そのまま出力 |
| 2 | Source | ソースフィルタ | VideoFilter (Category="Source") | FilterName(params) |
| 3 | Crop | クロップフィルタ | VideoFilter (Category="Crop") | Crop(left, top, -right, -bottom) |
| 4 | Field | デインターレースフィルタ | VideoFilter (Category="Field") | QTGMC(preset="...") |
| 5 | Noise | ノイズ除去フィルタ | VideoFilter (Category="Noise") | DFTTest(sigma=...) |
| 6 | Resize | リサイズフィルタ | VideoFilter (Category="Resize") | BicubicResize(width, height) |
| 7 | CodeAtBottom | ユーザー定義末尾コード | p.CodeAtBottom | そのまま出力 |

### フッター部

フッター部は存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| パス有効 | Path != "" | Yes |
| ソースフィルタ存在 | Filters(0).Category = "Source" | Yes |
| フィルタ有効 | filter.Active = True | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | フィルタ定義順 | 昇順（リスト順） |

### 改ページ条件

改ページなし（連続スクリプト）

## データベース参照仕様

### 参照テーブル一覧

本機能はデータベースを使用しない。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| - | - | - |

### 外部データソース

| ソース | 用途 | 取得方法 |
|--------|------|---------|
| Package.Items | プラグイン情報 | Package.Items.Values.OfType(Of PluginPackage) |
| FilterCategory | デフォルトフィルタ定義 | FilterCategory.GetAviSynthDefaults() |
| Project | プロジェクト設定 | p（グローバル変数） |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| - | - | - | スクリプト生成時の計算なし |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Synchronize呼び出し] --> B{Path有効?}
    B -->|No| Z[処理終了]
    B -->|Yes| C[GetScript呼び出し]
    C --> D[フィルタチェーン構築]
    D --> E[ModifyAVSScript呼び出し]
    E --> F[GetAvsLoadCode呼び出し]
    F --> G[プラグインロードコード生成]
    G --> H[スクリプト結合]
    H --> I[WriteFile出力]
    I --> J[フレームサーバー起動]
    J --> K[Info取得]
    K --> L[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ソースフィルタ不正 | Filters(0).Category != "Source" | "The first filter must be a source filter." | デフォルトフィルタにリセット |
| パッケージ未検証 | Package.AviSynth.VerifyOK = False | VerifyOKダイアログ表示 | AbortException |
| スクリプトエラー | フレームサーバー起動失敗 | server.Error に格納 | Error プロパティに設定 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数行〜数十行 |
| 目標出力時間 | 瞬時 |
| 同時出力数上限 | 1 |

## セキュリティ考慮事項

- ファイルパスがスクリプトに含まれるため、パス情報が露出する
- LoadPlugin/Import命令により任意のDLL/スクリプトが実行される可能性
- Macro.Expandによるマクロ展開が行われる

## 備考

- AviSynthスクリプトはVapourSynthと異なり、システムのコードページに依存したエンコーディングで出力される
- ポータブルモードの場合、AddAutoloadDirが自動的に追加される
- フィルタの依存関係（Dependencies）がある場合、依存プラグインも自動的にロードされる

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

VideoScript/VideoFilterクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | Line 12-26でVideoScriptクラスの基本構造を確認 |
| 1-2 | VideoScript.vb | `Source/Video/VideoScript.vb` | Line 840-887でVideoFilterクラスの構造を確認 |
| 1-3 | VideoScript.vb | `Source/Video/VideoScript.vb` | Line 1230-1233でScriptEngine列挙型を確認 |

**読解のコツ**: VideoScriptはProfileを継承し、Filtersリストにフィルタチェーンを保持する。Engine=ScriptEngine.AviSynthの場合にAviSynthスクリプトが生成される。

#### Step 2: エントリーポイントを理解する

スクリプト生成の起点を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | Synchronizeメソッド（Line 194-319）がスクリプト同期の入口 |

**主要処理フロー**:
1. **Line 199**: Path空チェック
2. **Line 204**: GetScript()でフィルタスクリプト取得
3. **Line 286-295**: ファイル書き込み（AviSynthはLine 294）
4. **Line 305-313**: フレームサーバー起動とInfo取得

#### Step 3: スクリプト生成処理を理解する

フィルタチェーンからスクリプトを生成する処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | GetScript（Line 51-75）でフィルタスクリプト結合を確認 |

**主要処理フロー**:
- **Line 58-60**: CodeAtTop追加
- **Line 62-68**: Activeフィルタのスクリプト追加
- **Line 70-72**: CodeAtBottom追加

#### Step 4: プラグインロード生成処理を理解する

自動プラグインロードコード生成を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | ModifyAVSScript（Line 671-709）でスクリプト修正を確認 |
| 4-2 | VideoScript.vb | `Source/Video/VideoScript.vb` | GetAvsLoadCode（Line 468-571）でプラグインロードコード生成を確認 |

**主要処理フロー**:
- **Line 468-473**: スクリプトから関数名を抽出
- **Line 475-567**: 各プラグインパッケージをチェックし、使用されている関数のプラグインをロード
- **Line 483-491**: DLLプラグインのLoadPlugin生成
- **Line 523-532**: AVSIスクリプトのImport生成

#### Step 5: ポータブルモード対応を理解する

ポータブルインストール時の特殊処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | Line 694-704でAddAutoloadDir追加を確認 |
| 5-2 | VideoScript.vb | `Source/Video/VideoScript.vb` | IsAvsPluginInAutoLoadFolder（Line 451-466）でオートロードフォルダチェックを確認 |

**主要処理フロー**:
- **Line 694**: FrameServerHelp.IsPortable判定
- **Line 695-704**: ポータブルモード時のプラグインフォルダ追加

#### Step 6: デフォルトフィルタ定義を理解する

デフォルトのAviSynthフィルタ設定を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | VideoScript.vb | `Source/Video/VideoScript.vb` | GetDefaults（Line 742-764）でデフォルトスクリプト定義を確認 |
| 6-2 | VideoScript.vb | `Source/Video/VideoScript.vb` | FilterCategory.GetAviSynthDefaults（Line 922-928）で埋め込みリソースからの読み込みを確認 |

**主要処理フロー**:
- **Line 745-752**: AviSynthデフォルトフィルタの定義
- **Line 922-928**: StaxRip.AviSynthFilterProfileDefaults.txtからの読み込み

### プログラム呼び出し階層図

```
VideoScript.Synchronize()
    │
    ├─ GetScript()
    │      ├─ p.CodeAtTop 追加
    │      ├─ Filters[].Script 追加（Active=Trueのみ）
    │      └─ p.CodeAtBottom 追加
    │
    ├─ ModifyAVSScript()
    │      │
    │      ├─ GetAvsLoadCode()
    │      │      ├─ FindFunctionsAVS() ← スクリプトから関数名抽出
    │      │      │
    │      │      └─ Package.Items.OfType(Of PluginPackage)
    │      │             ├─ LoadPlugin("plugin.dll") 生成
    │      │             └─ Import("script.avsi") 生成
    │      │
    │      └─ GetAVSLoadCodeFromImports() ← Importされたスクリプトの依存解決
    │
    ├─ WriteFile() ← TextEncoding.EncodingOfProcess
    │
    └─ FrameServerFactory.Create()
           └─ server.Info / server.Error 取得
```

### データフロー図

```
[入力]                    [処理]                         [出力]

p.CodeAtTop ─────────▶ GetScript() ─────────────▶ スクリプト本体
     │                       │
Filters[] ───────────────────┘
     │
p.CodeAtBottom ─────────────┘
                              │
                              ▼
Package.Items ──────────▶ GetAvsLoadCode() ──────▶ LoadPlugin/Import
                              │
                              ▼
                         ModifyAVSScript()
                              │
                              ▼
                         WriteFile() ──────────────▶ .avs ファイル
                              │                     (システムエンコーディング)
                              ▼
                         FrameServer起動 ─────────▶ Info/Error
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| VideoScript.vb | `Source/Video/VideoScript.vb` | ソース | スクリプト生成メインロジック |
| FrameServer.vb | `Source/Video/FrameServer.vb` | ソース | フレームサーバー基底クラス |
| Package.vb | （推定） | ソース | プラグインパッケージ管理 |
| CodeEditor.vb | `Source/Forms/CodeEditor.vb` | ソース | スクリプトエディタUI |
| AviSynthFilterProfileDefaults.txt | 埋め込みリソース | リソース | デフォルトフィルタ定義 |
| Project.vb | `Source/General/Project.vb` | ソース | Scriptプロパティ管理 |
