mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 15:37:49 +00:00
new tool server
This commit is contained in:
1 parent
9471f0e19a
commit
4c8a9dc62d
14 files changed
+925
-47
No files matched your search
Generated
+360
-47
@@ -420,6 +420,22 @@ dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agent-notify-server"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"env_logger 0.11.9",
|
||||
"log",
|
||||
"rodio",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.8"
|
||||
@@ -492,6 +508,18 @@ dependencies = [
|
||||
"nix 0.23.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
|
||||
dependencies = [
|
||||
"alsa-sys",
|
||||
"bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alsa-sys"
|
||||
version = "0.3.1"
|
||||
@@ -602,7 +630,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2610,6 +2638,17 @@ dependencies = [
|
||||
"coreaudio-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-sys"
|
||||
version = "0.2.17"
|
||||
@@ -2625,9 +2664,9 @@ version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"alsa 0.6.0",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-rs",
|
||||
"coreaudio-rs 0.10.0",
|
||||
"jni 0.19.0",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
@@ -2636,7 +2675,7 @@ dependencies = [
|
||||
"ndk 0.6.0",
|
||||
"ndk-glue",
|
||||
"nix 0.23.2",
|
||||
"oboe",
|
||||
"oboe 0.4.6",
|
||||
"parking_lot 0.11.2",
|
||||
"stdweb",
|
||||
"thiserror 1.0.63",
|
||||
@@ -2644,6 +2683,29 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpal"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"
|
||||
dependencies = [
|
||||
"alsa 0.9.1",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-rs 0.11.3",
|
||||
"dasp_sample",
|
||||
"jni 0.21.1",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"mach2",
|
||||
"ndk 0.8.0",
|
||||
"ndk-context",
|
||||
"oboe 0.6.1",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
@@ -2957,6 +3019,12 @@ dependencies = [
|
||||
"parking_lot_core 0.9.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dasp_sample"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.5.0"
|
||||
@@ -3291,7 +3359,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3996,6 +4064,12 @@ dependencies = [
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extended"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365"
|
||||
|
||||
[[package]]
|
||||
name = "fal_client"
|
||||
version = "0.0.1"
|
||||
@@ -6372,7 +6446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6665,6 +6739,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magic-crypt"
|
||||
version = "3.1.13"
|
||||
@@ -6812,7 +6895,7 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"errors",
|
||||
"log",
|
||||
"symphonia",
|
||||
"symphonia 0.5.0",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
@@ -7202,6 +7285,20 @@ dependencies = [
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"num_enum 0.7.2",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
@@ -7260,6 +7357,15 @@ dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.5.0+25.2.9519653"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
|
||||
dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.6.0+11769913"
|
||||
@@ -7789,7 +7895,21 @@ dependencies = [
|
||||
"ndk-context",
|
||||
"num-derive 0.3.3",
|
||||
"num-traits",
|
||||
"oboe-sys",
|
||||
"oboe-sys 0.4.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
|
||||
dependencies = [
|
||||
"jni 0.21.1",
|
||||
"ndk 0.8.0",
|
||||
"ndk-context",
|
||||
"num-derive 0.4.2",
|
||||
"num-traits",
|
||||
"oboe-sys 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7801,6 +7921,15 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe-sys"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
@@ -9674,6 +9803,16 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rodio"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1"
|
||||
dependencies = [
|
||||
"cpal 0.15.3",
|
||||
"symphonia 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rootly_client"
|
||||
version = "0.0.1"
|
||||
@@ -9887,7 +10026,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10004,7 +10143,7 @@ dependencies = [
|
||||
"security-framework 3.7.0",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11569,19 +11708,38 @@ name = "symphonia"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"symphonia-bundle-flac",
|
||||
"symphonia-bundle-mp3",
|
||||
"symphonia-codec-aac",
|
||||
"symphonia-bundle-flac 0.5.0",
|
||||
"symphonia-bundle-mp3 0.5.0",
|
||||
"symphonia-codec-aac 0.5.0",
|
||||
"symphonia-codec-alac",
|
||||
"symphonia-codec-opus",
|
||||
"symphonia-codec-pcm",
|
||||
"symphonia-codec-vorbis",
|
||||
"symphonia-core",
|
||||
"symphonia-format-isomp4",
|
||||
"symphonia-codec-pcm 0.5.0",
|
||||
"symphonia-codec-vorbis 0.5.0",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-format-isomp4 0.5.0",
|
||||
"symphonia-format-mkv",
|
||||
"symphonia-format-ogg",
|
||||
"symphonia-format-wav",
|
||||
"symphonia-metadata",
|
||||
"symphonia-metadata 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"symphonia-bundle-flac 0.5.5",
|
||||
"symphonia-bundle-mp3 0.5.5",
|
||||
"symphonia-codec-aac 0.5.5",
|
||||
"symphonia-codec-adpcm",
|
||||
"symphonia-codec-pcm 0.5.5",
|
||||
"symphonia-codec-vorbis 0.5.5",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-format-isomp4 0.5.5",
|
||||
"symphonia-format-riff",
|
||||
"symphonia-metadata 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11589,9 +11747,21 @@ name = "symphonia-bundle-flac"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
"symphonia-utils-xiph 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-flac"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-metadata 0.5.5",
|
||||
"symphonia-utils-xiph 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11601,8 +11771,20 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-mp3"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-metadata 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11611,7 +11793,28 @@ version = "0.5.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-aac"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-adpcm"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11619,7 +11822,7 @@ name = "symphonia-codec-alac"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11627,7 +11830,7 @@ name = "symphonia-codec-opus"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11635,7 +11838,17 @@ name = "symphonia-codec-pcm"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-pcm"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11643,8 +11856,19 @@ name = "symphonia-codec-vorbis"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-utils-xiph",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-utils-xiph 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-vorbis"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-utils-xiph 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11652,7 +11876,7 @@ name = "symphonia-codec-wavpack"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11666,15 +11890,41 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-core"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
"lazy_static",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-isomp4"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
"symphonia-utils-xiph 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-isomp4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-metadata 0.5.5",
|
||||
"symphonia-utils-xiph 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11683,9 +11933,9 @@ version = "0.5.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
"symphonia-utils-xiph 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11693,9 +11943,21 @@ name = "symphonia-format-ogg"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
"symphonia-utils-xiph 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-riff"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f"
|
||||
dependencies = [
|
||||
"extended",
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-metadata 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11703,8 +11965,8 @@ name = "symphonia-format-wav"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11714,7 +11976,19 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-core 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-metadata"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11722,22 +11996,32 @@ name = "symphonia-play"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"clap 3.2.25",
|
||||
"cpal",
|
||||
"cpal 0.13.5",
|
||||
"lazy_static",
|
||||
"libpulse-binding",
|
||||
"libpulse-simple-binding",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"rb",
|
||||
"symphonia",
|
||||
"symphonia 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-utils-xiph"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-core 0.5.0",
|
||||
"symphonia-metadata 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-utils-xiph"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16"
|
||||
dependencies = [
|
||||
"symphonia-core 0.5.5",
|
||||
"symphonia-metadata 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12285,7 +12569,7 @@ dependencies = [
|
||||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -13988,6 +14272,16 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
|
||||
dependencies = [
|
||||
"windows-core 0.54.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
@@ -14038,6 +14332,16 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
|
||||
dependencies = [
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
@@ -14152,6 +14456,15 @@ dependencies = [
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -150,6 +150,9 @@ members = [
|
||||
"crates/testing/test_data",
|
||||
"crates/testing/test_utils",
|
||||
|
||||
# Standalone tool servers
|
||||
"crates/tools/servers/agent_notify_server",
|
||||
|
||||
# Vendored code
|
||||
"crates/vendor/limitation",
|
||||
"crates/vendor/tauri-plugin-http",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
[package]
|
||||
name = "agent-notify-server"
|
||||
edition = "2021"
|
||||
version = "0.0.1"
|
||||
authors = ["Brandon Thomas <bt@brand.io>"]
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "agent-notify-server"
|
||||
path = "src/main.rs"
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
normal = ["workspace-build-acceleration"]
|
||||
|
||||
[dependencies]
|
||||
# Workspace hack (faster compile times)
|
||||
workspace-build-acceleration = { workspace = true }
|
||||
|
||||
# Web
|
||||
actix-web = { workspace = true }
|
||||
|
||||
# Async runtime
|
||||
tokio = { workspace = true, features = ["macros", "signal", "rt-multi-thread"] }
|
||||
|
||||
# Audio
|
||||
rodio = { version = "0.20.1", default-features = false, features = ["symphonia-all"] }
|
||||
|
||||
# Serialization
|
||||
serde = { workspace = true }
|
||||
serde_derive = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
|
||||
# Logging
|
||||
log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
|
||||
# Errors
|
||||
anyhow = { workspace = true }
|
||||
@@ -0,0 +1,13 @@
|
||||
# Configuration for the agent_notify_server.
|
||||
#
|
||||
# Each *_sound key may point to a .wav or .mp3 file. If a key is omitted (or
|
||||
# the file is missing) the corresponding endpoint becomes a no-op.
|
||||
#
|
||||
# `loop_alert_timeout_millis` is the gap *between* consecutive plays of a
|
||||
# looping sound (measured from the moment one play finishes to the moment the
|
||||
# next play begins). If omitted, looping sounds replay back-to-back.
|
||||
|
||||
alert_beep_sound: "/Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_ghost.wav"
|
||||
alert_done_sound: "/Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_ghost.wav"
|
||||
alert_await_user_input_sound: "/Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_ghost.wav"
|
||||
loop_alert_timeout_millis: 2000
|
||||
@@ -0,0 +1,211 @@
|
||||
//! Background-thread audio engine for the notify server.
|
||||
//!
|
||||
//! The engine runs on a dedicated OS thread that owns the cpal `OutputStream`
|
||||
//! (which is `!Send` on some platforms). All callers — including actix
|
||||
//! handlers — interact with it through [`AudioPlayerHandle`], which forwards
|
||||
//! commands over an mpsc channel.
|
||||
//!
|
||||
//! The engine maintains two independent mixing channels:
|
||||
//!
|
||||
//! - **`oneshot_sink`** — fire-and-forget sounds queued by `play_once`. Reused
|
||||
//! for the lifetime of the engine via `clear() + play()` so subsequent
|
||||
//! appends keep working after a `StopAll`.
|
||||
//! - **A per-loop iterator thread** — owns its own `Sink` and replays the
|
||||
//! configured sound, sleeping `gap_millis` between iterations. Replaced
|
||||
//! atomically when a new loop sound is requested; signalled to stop via
|
||||
//! `Arc<AtomicBool>` polled every 50ms (kept short so `/stop` and Ctrl+C
|
||||
//! feel instant).
|
||||
//!
|
||||
//! Because the two channels share the same `OutputStreamHandle`, a one-shot
|
||||
//! sound naturally mixes with whatever is looping.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink};
|
||||
|
||||
const STOP_POLL_INTERVAL: Duration = Duration::from_millis(50);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AudioPlayerHandle {
|
||||
tx: Sender<AudioCommand>,
|
||||
}
|
||||
|
||||
impl AudioPlayerHandle {
|
||||
pub fn play_once(&self, path: PathBuf) {
|
||||
let _ = self.tx.send(AudioCommand::PlayOnce(path));
|
||||
}
|
||||
|
||||
pub fn play_loop(&self, path: PathBuf, gap_millis: u64) {
|
||||
let _ = self.tx.send(AudioCommand::PlayLoop(path, gap_millis));
|
||||
}
|
||||
|
||||
pub fn stop_all(&self) {
|
||||
let _ = self.tx.send(AudioCommand::StopAll);
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) {
|
||||
let _ = self.tx.send(AudioCommand::Shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_audio_player() -> (AudioPlayerHandle, JoinHandle<()>) {
|
||||
let (tx, rx) = mpsc::channel::<AudioCommand>();
|
||||
let thread = thread::Builder::new()
|
||||
.name("agent-notify-audio".to_string())
|
||||
.spawn(move || run_audio_engine(rx))
|
||||
.expect("spawn audio engine thread");
|
||||
(AudioPlayerHandle { tx }, thread)
|
||||
}
|
||||
|
||||
enum AudioCommand {
|
||||
PlayOnce(PathBuf),
|
||||
PlayLoop(PathBuf, u64),
|
||||
StopAll,
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
fn run_audio_engine(rx: Receiver<AudioCommand>) {
|
||||
let (_stream, stream_handle) = match OutputStream::try_default() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("audio engine: failed to open default output stream: {}", e);
|
||||
// Drain the channel so callers don't block, then exit.
|
||||
while rx.recv().is_ok() {}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let oneshot_sink = match Sink::try_new(&stream_handle) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("audio engine: failed to create oneshot sink: {}", e);
|
||||
while rx.recv().is_ok() {}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut current_loop: Option<LoopController> = None;
|
||||
|
||||
while let Ok(cmd) = rx.recv() {
|
||||
match cmd {
|
||||
AudioCommand::PlayOnce(path) => {
|
||||
if let Err(e) = enqueue_sound(&oneshot_sink, &path) {
|
||||
log::warn!("play_once {}: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
AudioCommand::PlayLoop(path, gap_millis) => {
|
||||
stop_current_loop(&mut current_loop);
|
||||
current_loop = Some(start_loop(&stream_handle, path, gap_millis));
|
||||
}
|
||||
AudioCommand::StopAll => {
|
||||
stop_current_loop(&mut current_loop);
|
||||
oneshot_sink.clear();
|
||||
oneshot_sink.play();
|
||||
}
|
||||
AudioCommand::Shutdown => {
|
||||
stop_current_loop(&mut current_loop);
|
||||
oneshot_sink.stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Channel closed; clean up anyway.
|
||||
stop_current_loop(&mut current_loop);
|
||||
oneshot_sink.stop();
|
||||
}
|
||||
|
||||
fn start_loop(
|
||||
stream_handle: &OutputStreamHandle,
|
||||
path: PathBuf,
|
||||
gap_millis: u64,
|
||||
) -> LoopController {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let stop_for_thread = stop.clone();
|
||||
let stream_handle = stream_handle.clone();
|
||||
let thread = thread::Builder::new()
|
||||
.name("agent-notify-loop".to_string())
|
||||
.spawn(move || run_loop_iterator(stream_handle, path, gap_millis, stop_for_thread))
|
||||
.expect("spawn loop iterator thread");
|
||||
LoopController { stop, thread: Some(thread) }
|
||||
}
|
||||
|
||||
fn stop_current_loop(current_loop: &mut Option<LoopController>) {
|
||||
if let Some(mut lc) = current_loop.take() {
|
||||
lc.stop.store(true, Ordering::SeqCst);
|
||||
if let Some(thread) = lc.thread.take() {
|
||||
if let Err(e) = thread.join() {
|
||||
log::warn!("audio loop thread panicked while shutting down: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_loop_iterator(
|
||||
stream_handle: OutputStreamHandle,
|
||||
path: PathBuf,
|
||||
gap_millis: u64,
|
||||
stop: Arc<AtomicBool>,
|
||||
) {
|
||||
let sink = match Sink::try_new(&stream_handle) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::warn!("loop iterator: failed to create sink: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
while !stop.load(Ordering::SeqCst) {
|
||||
match enqueue_sound(&sink, &path) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::warn!("loop iterator: failed to enqueue {}: {}", path.display(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for playback to finish, but stay responsive to stop.
|
||||
while !sink.empty() {
|
||||
if stop.load(Ordering::SeqCst) {
|
||||
sink.stop();
|
||||
return;
|
||||
}
|
||||
thread::sleep(STOP_POLL_INTERVAL);
|
||||
}
|
||||
|
||||
if stop.load(Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if gap_millis > 0 {
|
||||
let total = Duration::from_millis(gap_millis);
|
||||
let started = Instant::now();
|
||||
while started.elapsed() < total {
|
||||
if stop.load(Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
let remaining = total.saturating_sub(started.elapsed());
|
||||
thread::sleep(remaining.min(STOP_POLL_INTERVAL));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enqueue_sound(sink: &Sink, path: &Path) -> anyhow::Result<()> {
|
||||
let file = BufReader::new(File::open(path)?);
|
||||
let source = Decoder::new(file)?;
|
||||
sink.append(source);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct LoopController {
|
||||
stop: Arc<AtomicBool>,
|
||||
thread: Option<JoinHandle<()>>,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
pub const DEFAULT_CONFIG_PATH: &str = "config/notify_config.yaml";
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct NotifyConfig {
|
||||
pub alert_beep_sound: Option<PathBuf>,
|
||||
pub alert_done_sound: Option<PathBuf>,
|
||||
pub alert_await_user_input_sound: Option<PathBuf>,
|
||||
pub loop_alert_timeout_millis: Option<u64>,
|
||||
}
|
||||
|
||||
impl NotifyConfig {
|
||||
pub fn read_from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
let file = File::open(&path)?;
|
||||
let reader = BufReader::new(file);
|
||||
let config: NotifyConfig = serde_yaml::from_reader(reader)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn read_from_file_or_default<P: AsRef<Path>>(path: P) -> Self {
|
||||
match Self::read_from_file(&path) {
|
||||
Ok(c) => {
|
||||
log::info!("loaded notify config from {}", path.as_ref().display());
|
||||
c
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"failed to load notify config from {}: {} — falling back to empty config",
|
||||
path.as_ref().display(),
|
||||
e
|
||||
);
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
pub async fn alert_beep_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_once_or_404(&state, state.config.alert_beep_sound.clone(), "alert_beep_sound")
|
||||
}
|
||||
|
||||
pub async fn alert_done_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_once_or_404(&state, state.config.alert_done_sound.clone(), "alert_done_sound")
|
||||
}
|
||||
|
||||
pub async fn alert_await_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_once_or_404(
|
||||
&state,
|
||||
state.config.alert_await_user_input_sound.clone(),
|
||||
"alert_await_user_input_sound",
|
||||
)
|
||||
}
|
||||
|
||||
fn play_once_or_404(
|
||||
state: &ServerState,
|
||||
maybe_path: Option<PathBuf>,
|
||||
config_key: &str,
|
||||
) -> HttpResponse {
|
||||
match maybe_path {
|
||||
Some(path) => {
|
||||
state.audio.play_once(path);
|
||||
HttpResponse::Ok().body("ok\n")
|
||||
}
|
||||
None => HttpResponse::NotFound().body(format!("{} not configured\n", config_key)),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
pub async fn loop_beep_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_loop_or_404(&state, state.config.alert_beep_sound.clone(), "alert_beep_sound")
|
||||
}
|
||||
|
||||
pub async fn loop_done_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_loop_or_404(&state, state.config.alert_done_sound.clone(), "alert_done_sound")
|
||||
}
|
||||
|
||||
pub async fn loop_await_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
play_loop_or_404(
|
||||
&state,
|
||||
state.config.alert_await_user_input_sound.clone(),
|
||||
"alert_await_user_input_sound",
|
||||
)
|
||||
}
|
||||
|
||||
fn play_loop_or_404(
|
||||
state: &ServerState,
|
||||
maybe_path: Option<PathBuf>,
|
||||
config_key: &str,
|
||||
) -> HttpResponse {
|
||||
match maybe_path {
|
||||
Some(path) => {
|
||||
let gap_millis = state.config.loop_alert_timeout_millis.unwrap_or(0);
|
||||
state.audio.play_loop(path, gap_millis);
|
||||
HttpResponse::Ok().body("ok\n")
|
||||
}
|
||||
None => HttpResponse::NotFound().body(format!("{} not configured\n", config_key)),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod alert_handlers;
|
||||
pub mod loop_handlers;
|
||||
pub mod root_handler;
|
||||
pub mod stop_handler;
|
||||
@@ -0,0 +1,9 @@
|
||||
use actix_web::{HttpResponse, Responder};
|
||||
|
||||
const INDEX_HTML: &str = include_str!("../../static/index.html");
|
||||
|
||||
pub async fn root_handler() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(INDEX_HTML)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
pub async fn stop_handler(state: web::Data<ServerState>) -> impl Responder {
|
||||
state.audio.stop_all();
|
||||
HttpResponse::Ok().body("ok\n")
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//! Tiny local HTTP server that plays configured notification sounds.
|
||||
//!
|
||||
//! Default bind address: `127.0.0.1:43110`. Override with `HTTP_BIND_ADDRESS`.
|
||||
//!
|
||||
//! Audio playback runs on a dedicated OS thread (see [`audio_player`]). The
|
||||
//! actix server itself is single-worker — we expect only the local agent to
|
||||
//! call it. On SIGINT we tell actix to skip its grace period and we ship a
|
||||
//! `Shutdown` command to the audio thread so any in-progress sound is dropped
|
||||
//! immediately.
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::middleware::Logger;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{App, HttpServer, web};
|
||||
|
||||
use crate::audio_player::spawn_audio_player;
|
||||
use crate::config::{DEFAULT_CONFIG_PATH, NotifyConfig};
|
||||
use crate::endpoints::alert_handlers::{
|
||||
alert_await_handler, alert_beep_handler, alert_done_handler,
|
||||
};
|
||||
use crate::endpoints::loop_handlers::{
|
||||
loop_await_handler, loop_beep_handler, loop_done_handler,
|
||||
};
|
||||
use crate::endpoints::root_handler::root_handler;
|
||||
use crate::endpoints::stop_handler::stop_handler;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
pub mod audio_player;
|
||||
pub mod config;
|
||||
pub mod endpoints;
|
||||
pub mod server_state;
|
||||
|
||||
const DEFAULT_BIND_ADDRESS: &str = "127.0.0.1:43110";
|
||||
const DEFAULT_RUST_LOG: &str = "info,actix_web=info";
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
if env::var_os("RUST_LOG").is_none() {
|
||||
// SAFETY: single-threaded at this point — no other thread can read env.
|
||||
unsafe { env::set_var("RUST_LOG", DEFAULT_RUST_LOG); }
|
||||
}
|
||||
env_logger::init();
|
||||
|
||||
let bind_address = env::var("HTTP_BIND_ADDRESS")
|
||||
.unwrap_or_else(|_| DEFAULT_BIND_ADDRESS.to_string());
|
||||
|
||||
let config_path: PathBuf = env::var("NOTIFY_CONFIG_PATH")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from(DEFAULT_CONFIG_PATH));
|
||||
|
||||
let config = Arc::new(NotifyConfig::read_from_file_or_default(&config_path));
|
||||
|
||||
let (audio_handle, audio_thread) = spawn_audio_player();
|
||||
|
||||
let state = ServerState {
|
||||
config: config.clone(),
|
||||
audio: audio_handle.clone(),
|
||||
};
|
||||
|
||||
log::info!("agent-notify-server listening on http://{}", bind_address);
|
||||
|
||||
let server = HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(Data::new(state.clone()))
|
||||
.wrap(Logger::default())
|
||||
.route("/", web::get().to(root_handler))
|
||||
.route("/alert_beep", web::get().to(alert_beep_handler))
|
||||
.route("/alert_done", web::get().to(alert_done_handler))
|
||||
.route("/alert_await", web::get().to(alert_await_handler))
|
||||
.route("/loop_beep", web::get().to(loop_beep_handler))
|
||||
.route("/loop_done", web::get().to(loop_done_handler))
|
||||
.route("/loop_await", web::get().to(loop_await_handler))
|
||||
.route("/stop", web::get().to(stop_handler))
|
||||
})
|
||||
.bind(&bind_address)?
|
||||
.workers(1)
|
||||
.shutdown_timeout(0)
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
log::info!("server stopped; shutting down audio engine");
|
||||
audio_handle.shutdown();
|
||||
if let Err(e) = audio_thread.join() {
|
||||
log::warn!("audio thread join failed: {:?}", e);
|
||||
}
|
||||
let _ = server;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::audio_player::AudioPlayerHandle;
|
||||
use crate::config::NotifyConfig;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServerState {
|
||||
pub config: Arc<NotifyConfig>,
|
||||
pub audio: AudioPlayerHandle,
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Agent Notify Server</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
max-width: 720px; margin: 40px auto; padding: 0 16px; color: #222; }
|
||||
h1 { margin-bottom: 4px; }
|
||||
.sub { color: #666; margin-top: 0; }
|
||||
code { background: #f3f3f3; padding: 1px 5px; border-radius: 3px; }
|
||||
table { border-collapse: collapse; width: 100%; margin-top: 18px; }
|
||||
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eee;
|
||||
vertical-align: top; }
|
||||
th { background: #fafafa; font-weight: 600; }
|
||||
.group { margin-top: 32px; }
|
||||
.pill { display: inline-block; padding: 1px 8px; border-radius: 10px;
|
||||
background: #eef; color: #224; font-size: 12px; margin-left: 6px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Agent Notify Server</h1>
|
||||
<p class="sub">Local audio-cue HTTP server for long-running agents.</p>
|
||||
|
||||
<div class="group">
|
||||
<h2>One-shot alerts</h2>
|
||||
<table>
|
||||
<tr><th>Endpoint</th><th>Behavior</th></tr>
|
||||
<tr><td><code>GET /alert_beep</code></td><td>Play <em>alert_beep</em> once.</td></tr>
|
||||
<tr><td><code>GET /alert_done</code></td><td>Play <em>alert_done</em> once.</td></tr>
|
||||
<tr><td><code>GET /alert_await</code></td><td>Play <em>alert_await_user_input</em> once.</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<h2>Looping alerts <span class="pill">replaces existing loop</span></h2>
|
||||
<table>
|
||||
<tr><th>Endpoint</th><th>Behavior</th></tr>
|
||||
<tr><td><code>GET /loop_beep</code></td><td>Loop <em>alert_beep</em>. Replaces any prior loop.</td></tr>
|
||||
<tr><td><code>GET /loop_done</code></td><td>Loop <em>alert_done</em>. Replaces any prior loop.</td></tr>
|
||||
<tr><td><code>GET /loop_await</code></td><td>Loop <em>alert_await_user_input</em>. Replaces any prior loop.</td></tr>
|
||||
</table>
|
||||
<p style="color:#555;font-size:14px;">
|
||||
Between iterations the server waits <code>loop_alert_timeout_millis</code>
|
||||
(from the YAML config) before playing again.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<h2>Stop</h2>
|
||||
<table>
|
||||
<tr><th>Endpoint</th><th>Behavior</th></tr>
|
||||
<tr><td><code>GET /stop</code></td><td>Stop all playback (loops and queued one-shots).</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<h2>Mixing rules</h2>
|
||||
<ul>
|
||||
<li>A one-shot mixes with whatever is looping — both play simultaneously.</li>
|
||||
<li>Requesting a new loop replaces the prior loop.</li>
|
||||
<li><code>/stop</code> halts everything.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user