Open12

YMM4 Plugin 制作メモ

いぬいぬいぬいぬ

VSCode + dotnet cliで開発する

公式には本家VSの開発情報だけだが、csprojの書き方工夫でVSCode+dotnet cliでもプラグイン作れる。

csprojの書き方

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <!-- YMM4 plugins -->
    <Reference Include="path\to\YukkuriMovieMaker4\YukkuriMovieMaker.Plugin.dll" />
    <Reference Include="path\to\YukkuriMovieMaker4\YukkuriMovieMaker.Controls.dll" />
    <!-- 環境変数を使う場合
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Plugin.dll" />
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Controls.dll" />
    -->
    <!-- ビルド時にdllを展開しない -->
    <!-- 
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Plugin.dll">
      <Private>false</Private>
      <CopyLocal>false</CopyLocal>
    </Reference>
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Controls.dll">
      <Private>false</Private>
      <CopyLocal>false</CopyLocal>
    </Reference>
      -->
  </ItemGroup>
</Project>

cliコマンドdotnet add referenceでいけるかどうかは不明なので手書き。

パス

YMM4のメニューの「ヘルプ」>「その他」>「アプリケーションフォルダを開く」でYukkuriMovieMaker.Plugin.dllへのパスを取得。

環境変数などに登録しておくと良い

  • 例:YMM4_PATH
  • csproj内では $(YMM4_PATH)でパスが参照できる
csprj
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Plugin.dll" />
    <Reference Include="$(YMM4_PATH)\YukkuriMovieMaker.Controls.dll" />

SampleSAPI5VoicePluginはdotnet cliでビルドできない

manju-summoner/YukkuriMovieMaker4PluginSamples: YMM4用プラグインのサンプル集です

以下にあるように、利用しているmsbuildが.NET Framework版である必要がある。そのままのdotnet buildでは.NET Core系のmsbuildが呼ばれるため。

https://qiita.com/up-hash/items/c3b813a24cd8d0247855

https://learn.microsoft.com/ja-jp/visualstudio/msbuild/resolvecomreference-task?view=vs-2022

いぬいぬいぬいぬ

.NET SDK 8.0 移行

YMM v4.23.0.0で.NET7から.NET8に移行したため、プロジェクトの<TargetFramework>をnet7.0-windows10.0.19041.0からnet8.0-windows10.0.19041.0に変更する必要があります。

ゆっくりMovieMaker v4.22.x.x以前に作成されたプロジェクトファイルの移行手順

未検証だけど <TargetFrameworks>で.NET7/8両対応ビルドできるかも?

<TargetFrameworks>net7.0-windows10.0.19041.0;net8.0-windows10.0.19041.0;</TargetFrameworks>

dllの依存関係でダメかな?
参照先を変えれば?

→ だめだった…

いぬいぬいぬいぬ

dotnet publish で ymme をつくる

やりかた

  • MSBuildをつかってpublish -> zip圧縮 -> ymmeリネーム -> プラグインフォルダへ配置まで一括でやる

csproj

  • $(YMM4_PATH)は環境変数で通しておく

  • ビルド前に出力先をクリアしておくTarget

csproj ビルド前に出力先をクリアしておく(古いバイナリ残らない様に)
 <Target Name="RemovePublishDirBeforeBuild" BeforeTargets="BeforeBuild">
    <RemoveDir Directories="$(OutputPath)/publish/" />
    <RemoveDir Directories="$(YMM4_PATH)\user\plugin\$(AssemblyName)\"/>
    <Message Text="RemovePublishDirBeforeBuild" Importance="high" />
  </Target>
  • publish後にいろいろするTarget
publish後の処理
  <Target Name="MakeZipPackage" AfterTargets="Publish">
    <!-- 一旦ymmeの出力先 -->
    <MakeDir Directories="$(OutputPath)/../../../../../publish/" />

    <ItemGroup>
      <!-- 不要なファイルをymmeに取り込まない様にここで指定する -->
      <FilesToDelete Include="$(OutputPath)\publish\YukkuriMovieMaker.*.dll"/>
    </ItemGroup>

    <Delete Files="@(FilesToDelete)">
       <Output
          TaskParameter="DeletedFiles"
          ItemName="FilesDeleted"/>
     </Delete>

    <!-- zip圧縮、ymme拡張子変更 -->
    <ZipDirectory
      SourceDirectory="$(OutputPath)/publish/"
      DestinationFile="$(OutputPath)/../../../../../publish/$(AssemblyName).ymme"
      Overwrite="true" />
    <Message Text="Actions After Publish" Importance="high" />

    <!-- ※ここから下はやらなくてもymmeは既にできてる。そのままテストしたい場合用 -->
    <!-- プラグインフォルダ以下にコピー -->
    <Copy
      SourceFiles="$(OutputPath)/../../../../../publish/$(AssemblyName).ymme"
      DestinationFolder="$(YMM4_PATH)\user\plugin\"
      />
    <!-- ymmeを展開して使えるようにする -->
    <Unzip
      SourceFiles="$(OutputPath)/../../../../../publish/$(AssemblyName).ymme" DestinationFolder="$(YMM4_PATH)\user\plugin\$(AssemblyName)\" />
  </Target>
いぬいぬいぬいぬ

素材一覧に製作者・ニコニコIDを表示する

プラグイン本体

PluginDetails属性
[PluginDetails(AuthorName = "YourName", ContentId = "YourNicoNicoID")]
public MyPlugin: IPlugin {}

または

IPlugin.Details を実装する
public PluginDetailsAttribute Details => new()
{
    //制作者
    AuthorName = "InuInu",
    //作品ID
    ContentId = "",
};

※PluginDetails属性だけだと反映されないバグ? or プラグインの種類がある模様

その場合、面倒なのでリフレクションで取得。

public PluginDetailsAttribute Details
		=> GetType().GetCustomAttribute<PluginDetailsAttribute>()
			?? new();

ボイス(声質)

IVoiceSpeaker
//ボイス(声質)の制作者
public string? SpeakerAuthor { get; } = "test";
//ボイス(声質)の作品ID (ニコニコIDとして有効じゃないとダメ)
public string? SpeakerContentId { get; } = "nc9999999";
//合成エンジン(アプリケーション)の制作者
public string? EngineAuthor { get; } = "test3";
//合成エンジン(アプリケーション)の作品ID (ニコニコIDとして有効じゃないとダメ)
public string? EngineContentId { get; } = "nc9999999";

VideoEffect、AudioEffect

PluginDetails属性

いぬいぬいぬいぬ

デバッグ(YMM4 + VSCode)

以下の設定でデバッガー接続できる。

launch.json

  • 環境変数 YMM_PATH は以下のように ${env:YMM4_PATH}で参照可能。
  • "justMyCode": falseが無いとたぶんダメ。
{
    "name": "Debug YMM4",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "build",
    "program": "${env:YMM4_PATH}/YukkuriMovieMaker.exe",
    "args": [],
    "cwd": "${workspaceFolder}",
    "stopAtEntry": false,
    "console": "internalConsole",
    "justMyCode": false,
    "logging": {
        "moduleLoad": false
    }
}

csproj

  • dotnet build時にdllがYMM4のプラグインフォルダ以下にコピーすればデバッグできる
    • pdbも必要
    • ライブラリが使っているdllなどすべてがbin/Debug以下にあるわけではないのでpublishしたときのものを追加でコピーする
      • 例:bin/Release/publish 以下のdllを全部持ってくる
      • 注:publishした時pdbを消す処理していると除外されるのでどうにかする
csproj
<Target Name="CopyDebugDlls" BeforeTargets="AfterBuild" Condition="'$(Configuration)' == 'Debug'">
    <ItemGroup>
      <CommonPaths Include="$(OutputPath)../../Release/$(TargetFramework)/publish"/>

      <MissingDlls Include="$(CommonPaths)/*.dll" Exclude="$(CommonPaths)/$(AssemblyName).dll"/>
      <MissingPdbs Include="$(CommonPaths)/*.pdb" Exclude="$(CommonPaths)/$(AssemblyName).pdb"/>
    </ItemGroup>
    <Copy SourceFiles="@(MissingDlls)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true"/>
    <Copy SourceFiles="@(MissingPdbs)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true"/>

    <ItemGroup>
      <SourceFiles Include="$(OutputPath)**/*.*" />
    </ItemGroup>
    <Copy SourceFiles="@(SourceFiles)" DestinationFolder="$(YMM4_PATH)\user\plugin\$(AssemblyName)\" />
  </Target>

参考

いぬいぬいぬいぬ

プラグインの動的なUI付きパラメータ

  • パラメータをコレクションで持たせてカスタムコントロールを作るケース
  • パラメータをコレクションで持たせてカスタム属性で表示するケース
    • 入れ子パラメータ内で既存のTextBoxSliderとか使いたい場合
      がある(後者は饅頭遣いさんから教えていただいた)

コレクションパラメータ

[Display(AutoGenerateField = true)]で入れ子内の表示を自動生成に任せる

MyParentParameter.cs
[Display(AutoGenerateField = true)]
public ImmutableList<MyChildParameter> ItemsCollection {/*...*/}

カスタム属性

  • CustomDisplayAttributeBaseを継承した属性を作成。
    • リフレクションでごにょごにょして表示させる
  • カスタム表示させたいプロパティに付与
MyChildParameter.cs
[MyCustomDisplay]
[TextBoxSlider("F2", "", 0, 1, Delay = -1)]
[Range(0, 1)]
[DefaultValue(0.0)]
public double Value { get => _value; set => Set(ref _value, value);}
いぬいぬいぬいぬ

Windowsタスクバーに進捗表示

var main = Application.Current.MainWindow;
var taskbar = new TaskbarItemInfo
{
    ProgressState = TaskbarItemProgressState.Indeterminate,
};
main.TaskbarItemInfo = taskbar;

//do something...

taskbar.ProgressState = TaskbarItemProgressState.None;
  • Application.Current.MainWindow でYMM4のメインウィンドウを取得

  • TaskbarItemInfoに割り当てる

  • ProgressStateで進捗表示。ProgressValueで比率も指定できる。

  • 注:UIスレッドの処理になるのでワーカースレッドから呼ぶ場合は注意が必要

    • EpoxyのUIThreadクラスなどを使うと楽
//EpoxyのUIThreadクラスを使う(この外はワーカースレッドなので呼べない)
await UIThread.InvokeAsync(()=>{
    //ここはUIスレッドになってるのでここで呼び出す
    TaskbarUtil.StartIndeterminate();
    return ValueTask.CompletedTask;
}).ConfigureAwait(false);

いぬいぬいぬいぬ

YMM4プラグイン用githubのテンプレートリポジトリ

https://github.com/InuInu2022/ymme-vscode-boilerplate

  • src/Sample以下に何もしないサンプルプラグインが設定済みです
    • これをもとにしても、公式のサンプルを元にしてもOK
    • ソリューションファイル:Sample.slnも設定済みです
  • dotnet publishコマンドでymmeファイルをpublish以下に作成&YMM4のプラグインフォルダ以下に展開できます
  • Code Analyzerを色々有効にしています
  • ビルド時に LICENSEREADME.md がdllやymmeに含まれるように設定済
  • licensesフォルダ以下のファイルもdllやymmeに含まれるように
  • MinVerライブラリでgitのタグから自動でSemVerが付きます
  • VSCode: VSCodeのpublishタスクでも上記ができるようにしています
  • VSCode: プラグインをYMM4ごとデバッガ接続で起動することができます
  • gitignore, editorconfig設定済み

https://docs.github.com/ja/repositories/creating-and-managing-repositories/creating-a-template-repository

いぬいぬいぬいぬ

FocusHelper

  • YukkuriMovieMaker.Commons.FocusHelper
    • 外部のアプリ操作のときなどにYMM4にフォーカスを戻すことができる
//現在のフォーカスされたUI部品(WPFの`FrameworkElement`)を取得できる
FocusHelper.DefaultFocus;

//YMM4のウィンドウ内の要素にフォーカスを移す
FocusHelper.FocusWindowContent(DependencyObject dependencyObject);

//外部アプリなどにフォーカスが移ったあとにYMM4にフォーカスを戻す
Window.GetWindow(FocusHelper.DefaultFocus).Activate();
FocusHelper.FocusWindowContent(FocusHelper.DefaultFocus);
いぬいぬいぬいぬ

IToolPlugin

IToolPluginを実装したプラグインを作り、View(UserControl)とViewModelの型を渡すと、YMM4のツールメニューから呼び出し可能なツールが作れる。

public class MyToolPlugin : IToolPlugin
{
	public string Name => "MyToolPlugin";

	public Type ViewModelType
		=> typeof(MyToolViewModel);
	public Type ViewType
		=> typeof(MyToolView);
}

ただし、YMM4内部のAPIとかは他の形式のプラグイン以下の事しかできないため、
ある程度独自処理のツールしか作れない。