Skip to content

Commit

Permalink
fix #37 (#38)
Browse files Browse the repository at this point in the history
設定ファイルの run_with を実装して、VACの起動時に関連アプリを自動的に起動したり、URLを開いたりできるようにします
  • Loading branch information
usagi authored Oct 14, 2023
1 parent 5b1ad48 commit 02bedb2
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 11 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ whatlang = "0.16.3"
isolang = { version = "2.3.0", features = ["list_languages"] }
os_info = "3.7.0"
async-recursion = "1.0.5"
sysinfo = "0.29.10"
webbrowser = "0.8.12"
duct = "0.13.6"
runas = "1.1.0"

# Windows Only
[target.'cfg(windows)'.dependencies]
Expand Down
27 changes: 27 additions & 0 deletions conf.example--run-with.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# run_with のお試し用設定ファイルです。
# お試し方法: virtual-avatar-connect.exe にこのファイルをドラッグ&ドロップまたは、コマンドライン引数で指定して起動してください。
# ※commandは実際にお試しになる環境で実行可能なコマンドに書き換えるなどしてからお試し下さい。
# 関連Issue: https://github.com/usagi/virtual-avatar-connect/issues/37

run_with = [
# どうあれメモ帳を起動します。
"notepad",

# どうあれURLを開きます。
"http://127.0.0.1:57000/input",

# まだ動作していない場合のみ、CoeiroInk を起動します。
{ command = '''C:\Users\the\app\COEIROINK_WIN_GPU_v.2.1.1\COEIROINKv2.exe''', if_not_running = "COEIROINKv2" },

# まだ動作していない場合のみ、VirtualMotionCapture を起動します。
{ command = '''C:\Users\the\app\vmc\VirtualMotionCapture.exe''', if_not_running = "VirtualMotionCapture" },

# まだ動作していない場合のみ、WebcamMotionCapture を起動します。
{ command = '''C:\Users\the\app\WebcamMotionCapture_Win\bin\webcam_motion_capture\webcam_motion_capture.exe''', if_not_running = "webcam_motion_capture" },

# まだ動作していない場合のみ、VMagicMirror を起動します。
{ command = '''"C:\Program Files (x86)\VMagicMirror\VMagicMirror.exe"''', if_not_running = "VMagicMirror" },

# まだ動作していない場合のみ、OBS Studio を管理者権限での実行をユーザーに求め、かつ作業ディレクトリーを指定して起動します。
{ command = '''C:\Program Files\obs-studio\bin\64bit\obs64.exe''', if_not_running = "obs64.exe", run_as_admin = true, working_dir = '''C:\Program Files\obs-studio\bin\64bit''' },
]
6 changes: 6 additions & 0 deletions conf.example-command.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 《Command》 のお試し用設定ファイルです。
# お試し方法: virtual-avatar-connect.exe にこのファイルをドラッグ&ドロップまたは、コマンドライン引数で指定して起動してください。
# ※必要に応じて環境にあわせた設定値に変更してお試し下さい。
# 関連Issue: https://github.com/usagi/virtual-avatar-connect/issues/14


# 音声から「かっこよくそれっぽいコマンド」を演出します。(必須ではありませんが心躍る方は参考にして下さい。)
[[processors]]
feature = "modify"
Expand Down
16 changes: 6 additions & 10 deletions conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ workers = 8
# デフォルト: "resources"
# web_ui_resources_path = "resources"

# ここで指定したコマンドたちは VAC の起動時に自動的に実行されます。
# 配信用に同時に使いたいアプリや、開いておきたい URL を設定しておくと便利です。
# ※ Windows で使う場合は
# 例: "start http://127.0.0.1:57000/input"
# のように設定すると URL を開くのも簡単です。
# run_with = ["start http://127.0.0.1:57000/input"]
# run_with を設定すると VAC の起動時に他のプログラムを起動したり、URLを開いたりできます。
# 使い方: conf.example--run_with.conf お試し設定ファイルを参考に設定してみて下さい。
# 既に起動中なら起動しない、管理者権限で実行(UACあり)などの設定もできます。
# 配信用の関連アプリをまとめて起動する設定などに使えます。
# run_with = []

# VAC はオープンソースソフトウェアです。基本的には開発にご協力頂ける方向けのデバッグ出力用のオプションです。
# 通常は設定する必要はありませんが、黒い画面で文字がたくさん流れるのを眺めたい方は TRACE や DEBUG を設定してみてください。
Expand Down Expand Up @@ -142,10 +141,7 @@ channel_to = "ai-synth"
# ほかにも「です」「ます」→「ですにゃ」「ますにゃ」のような使い方もわりと実用性が高いかもしれません。
# 設定は配列なので複数のファイルを指定できます。もちろん1つから使えます。
# 先に指定してあるほど優先度が高くなります。
dictionary_files = [
"dictionary.arknights.txt",
"dictionary.pre-coeiroink.txt",
]
dictionary_files = ["dictionary.arknights.txt", "dictionary.pre-coeiroink.txt"]

# true にすると英語の単語をカタカナに変換します。
alkana = true
Expand Down
94 changes: 93 additions & 1 deletion src/conf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ pub type SharedConf = Arc<RwLock<Conf>>;

pub const DEFAULT_WEB_UI_ADDRESS: &str = "127.0.0.1:57000";

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum RunWith {
Command(String),
CommandIfProcessIsNotRunning {
command: String,
if_not_running: Option<String>,
run_as_admin: Option<bool>,
working_dir: Option<String>,
},
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Conf {
pub workers: Option<usize>,
Expand All @@ -26,7 +38,7 @@ pub struct Conf {
pub state_data_pretty: Option<bool>,

#[serde(default)]
pub run_with: Vec<String>,
pub run_with: Vec<RunWith>,

pub log_level: Option<String>,

Expand Down Expand Up @@ -74,6 +86,75 @@ impl Conf {
Ok(conf)
}

pub fn execute_run_with(&self) -> Result<()> {
use sysinfo::{ProcessExt, ProcessRefreshKind, SystemExt};
let mut system = sysinfo::System::new();
system.refresh_processes_specifics(ProcessRefreshKind::everything().without_cpu());

for run_with in self.run_with.iter() {
let (command, if_not_running, run_as_admin, working_dir) = match run_with {
RunWith::Command(command) => (command, None, false, None),
RunWith::CommandIfProcessIsNotRunning {
command,
if_not_running,
run_as_admin,
working_dir,
} => (
command,
if_not_running.as_ref(),
run_as_admin.unwrap_or_default(),
working_dir.as_ref(),
),
};

// if_not_running が指定されている場合は、プロセスが実行中か確認して実行中ならスキップ
if let Some(if_not_running) = if_not_running {
if system
.processes()
.iter()
.any(|(_, process)| process.name().contains(if_not_running))
{
log::info!(
"run_with: 既に {} を含むプロセスが実行中のため {} の実行はスキップされます。",
if_not_running,
command
);
continue;
}
}

// command (引数がある場合も考慮)を実行、またはURLを開く
if command.starts_with("http://") || command.starts_with("https://") {
use webbrowser::{Browser, BrowserOptions};
log::info!("run_with: {:?} を URL としてブラウザーで開きます。", command);
if let Err(e) = webbrowser::open_browser_with_options(Browser::Default, command, BrowserOptions::new().with_target_hint("vac")) {
log::error!("run_with: URL を開く際にエラーが発生しました: {:?}", e);
}
} else {
let original_dir = match change_working_dir(working_dir) {
Ok(original_dir) => original_dir.map(|p| p.to_string_lossy().to_string()),
Err(e) => {
log::error!("run_with: 作業ディレクトリーの変更に失敗しました: {:?}", e);
continue;
},
};
if run_as_admin {
log::warn!("run_with: {:?} を管理者権限で実行を試みます。", command);
if let Err(e) = runas::Command::new(command).status() {
log::error!("run_with: 管理者権限でコマンドを実行する際にエラーが発生しました: {:?}", e);
}
} else {
log::info!("run_with: {:?} をコマンドとして実行します。", command);
if let Err(e) = duct::cmd!(command).start() {
log::error!("run_with: コマンドを実行する際にエラーが発生しました: {:?}", e);
}
}
change_working_dir(original_dir.as_ref())?;
}
}
Ok(())
}

pub fn to_shared(self) -> SharedConf {
Arc::new(RwLock::new(self))
}
Expand All @@ -93,3 +174,14 @@ impl Conf {
fn default_web_ui_resources_path() -> Option<String> {
Some("resources".to_string())
}

/// 現在の作業ディレクトリを変更して、変更前の作業ディレクトリを返します。
fn change_working_dir(working_dir: Option<&String>) -> Result<Option<std::path::PathBuf>> {
if let Some(working_dir) = working_dir {
let original_dir = std::env::current_dir()?;
std::env::set_current_dir(working_dir)?;
Ok(Some(original_dir))
} else {
Ok(None)
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub async fn run() -> Result<()> {
let args = Args::init(audio_sink.clone()).await?;
// 設定を読み込みし、ログレベルを更新
let conf = Conf::new(&args)?;
// run_with の実行
conf.execute_run_with()?;
// 共有ステートを作成
let state = State::new(&conf, audio_sink).await?;

Expand Down

0 comments on commit 02bedb2

Please sign in to comment.