Començons un Mediarenderer
This commit is contained in:
353
Cargo.lock
generated
353
Cargo.lock
generated
@@ -63,6 +63,17 @@ dependencies = [
|
|||||||
"derive_arbitrary",
|
"derive_arbitrary",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_type_match"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -231,11 +242,103 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_macro_utils"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d61e64e7431f7349b5caa1ea4ff2efd94afd73480fac2f44a2b94b1642b4a02a"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_platform"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "239c3f54e9e25a01e4d0f54a3a066e1d8d6d5509b071a78c729c1e0143db7f4b"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"foldhash",
|
||||||
|
"futures-channel",
|
||||||
|
"hashbrown",
|
||||||
|
"js-sys",
|
||||||
|
"portable-atomic",
|
||||||
|
"portable-atomic-util",
|
||||||
|
"serde",
|
||||||
|
"spin",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_ptr"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b3533a9a9c2158bc704c18ba26f0f0692d0423ff0a10b656b8db46a9b953620"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_reflect"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18f363a1e984a5b9e908e9b98181e396d4a93ee8209a698c0d0e38227f464e45"
|
||||||
|
dependencies = [
|
||||||
|
"assert_type_match",
|
||||||
|
"bevy_platform",
|
||||||
|
"bevy_ptr",
|
||||||
|
"bevy_reflect_derive",
|
||||||
|
"bevy_utils",
|
||||||
|
"derive_more",
|
||||||
|
"disqualified",
|
||||||
|
"downcast-rs",
|
||||||
|
"erased-serde",
|
||||||
|
"foldhash",
|
||||||
|
"glam",
|
||||||
|
"inventory",
|
||||||
|
"serde",
|
||||||
|
"smallvec",
|
||||||
|
"smol_str",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
"variadics_please",
|
||||||
|
"wgpu-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_reflect_derive"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d65b08cc2fb06b872143c506ee7e14decb3ade19bf25af9ddd39dfc1799ccc85"
|
||||||
|
dependencies = [
|
||||||
|
"bevy_macro_utils",
|
||||||
|
"indexmap",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_utils"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6927fae6678c8ca2984b97d1a77bb244cfe095f5fd28ed2f88e0d66cfab938fa"
|
||||||
|
dependencies = [
|
||||||
|
"bevy_platform",
|
||||||
|
"disqualified",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.4"
|
version = "2.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
@@ -252,6 +355,26 @@ version = "3.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.23.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck_derive"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
@@ -318,6 +441,12 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -374,6 +503,26 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -416,12 +565,35 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "disqualified"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "erased-serde"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"typeid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -445,6 +617,12 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -620,6 +798,15 @@ version = "0.32.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glam"
|
||||||
|
version = "0.30.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -644,6 +831,10 @@ name = "hashbrown"
|
|||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
@@ -884,6 +1075,15 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inventory"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
@@ -1116,6 +1316,18 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pmodidl"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bevy_reflect",
|
||||||
|
"bevy_reflect_derive",
|
||||||
|
"quick-xml",
|
||||||
|
"serde",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pmoupnp"
|
name = "pmoupnp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1126,6 +1338,8 @@ dependencies = [
|
|||||||
"axum-embed",
|
"axum-embed",
|
||||||
"axum-server",
|
"axum-server",
|
||||||
"base64",
|
"base64",
|
||||||
|
"bevy_reflect",
|
||||||
|
"bevy_reflect_derive",
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1135,6 +1349,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pmoconfig",
|
"pmoconfig",
|
||||||
|
"pmodidl",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1158,6 +1373,21 @@ dependencies = [
|
|||||||
"get_if_addrs",
|
"get_if_addrs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic-util"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -1198,6 +1428,16 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.38.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.41"
|
version = "1.0.41"
|
||||||
@@ -1461,6 +1701,15 @@ version = "1.15.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smol_str"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -1471,6 +1720,15 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -1604,6 +1862,36 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.23.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -1694,6 +1982,12 @@ dependencies = [
|
|||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeid"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
@@ -1786,6 +2080,7 @@ checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1825,6 +2120,17 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "variadics_please"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -1892,6 +2198,19 @@ dependencies = [
|
|||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.104"
|
version = "0.2.104"
|
||||||
@@ -1924,6 +2243,31 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-types"
|
||||||
|
version = "26.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bytemuck",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@@ -2089,6 +2433,15 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["PMOMusic", "pmoupnp","pmoconfig", "pmoutils"]
|
members = ["PMOMusic", "pmoupnp","pmoconfig", "pmoutils", "pmodidl"]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use pmoupnp::server::{
|
use pmoupnp::{mediarenderer::avtransport::actions::{SETAVTRANSPORTURI}, server::{
|
||||||
ServerBuilder, Webapp,
|
logs::{log_dump, log_sse, LogState, SseLayer}, ServerBuilder, Webapp
|
||||||
logs::{LogState, SseLayer, log_dump, log_sse},
|
}, UpnpXml}; // ton module pmoupnp::server
|
||||||
}; // ton module pmoupnp::server
|
|
||||||
use tracing_subscriber::Registry;
|
use tracing_subscriber::Registry;
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -47,6 +47,8 @@ async fn main() {
|
|||||||
|
|
||||||
server.add_redirect("/", "/app").await;
|
server.add_redirect("/", "/app").await;
|
||||||
|
|
||||||
|
info!("{}",SETAVTRANSPORTURI.to_markdown());
|
||||||
|
|
||||||
server.start().await;
|
server.start().await;
|
||||||
server.wait().await;
|
server.wait().await;
|
||||||
}
|
}
|
||||||
|
|||||||
12
pmodidl/Cargo.toml
Normal file
12
pmodidl/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "pmodidl"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1.0.228"
|
||||||
|
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||||
|
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||||
|
quick-xml = { version = "0.38.3", features = ["serialize"] }
|
||||||
|
bevy_reflect = "0.17.1"
|
||||||
|
bevy_reflect_derive = "0.17.1"
|
||||||
580
pmodidl/src/lib.rs
Normal file
580
pmodidl/src/lib.rs
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
//! # pmodidl - DIDL-Lite Parser
|
||||||
|
//!
|
||||||
|
//! Parser et utilitaires pour le format DIDL-Lite utilisé dans UPnP/DLNA.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Write;
|
||||||
|
use bevy_reflect::Reflect;
|
||||||
|
|
||||||
|
// ============= Couche d'abstraction générique =============
|
||||||
|
|
||||||
|
/// Trait pour tout parser de métadonnées média
|
||||||
|
pub trait MediaMetadataParser: Sized {
|
||||||
|
type Error: std::error::Error + Send + Sync + 'static;
|
||||||
|
|
||||||
|
/// Parse une chaîne de métadonnées
|
||||||
|
fn parse(input: &str) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
|
/// Retourne le format du parser
|
||||||
|
fn format_name() -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enveloppe générique pour tout type de métadonnées parsées
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Reflect)]
|
||||||
|
pub struct ParsedMetadata<T> {
|
||||||
|
/// Format du document (ex: "DIDL-Lite", "RSS", etc.)
|
||||||
|
pub format: String,
|
||||||
|
|
||||||
|
/// Données parsées
|
||||||
|
pub data: T,
|
||||||
|
|
||||||
|
/// Timestamp du parsing (exclu de la réflexion car SystemTime n'implémente pas Reflect)
|
||||||
|
#[reflect(ignore)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parsed_at: Option<std::time::SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ParsedMetadata<T> {
|
||||||
|
pub fn new(format: impl Into<String>, data: T) -> Self {
|
||||||
|
Self {
|
||||||
|
format: format.into(),
|
||||||
|
data,
|
||||||
|
parsed_at: Some(std::time::SystemTime::now()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforme les données avec une fonction
|
||||||
|
pub fn map<U, F>(self, f: F) -> ParsedMetadata<U>
|
||||||
|
where
|
||||||
|
F: FnOnce(T) -> U,
|
||||||
|
{
|
||||||
|
ParsedMetadata {
|
||||||
|
format: self.format,
|
||||||
|
data: f(self.data),
|
||||||
|
parsed_at: self.parsed_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fonction helper pour parser et envelopper automatiquement
|
||||||
|
pub fn parse_metadata<P: MediaMetadataParser>(input: &str) -> Result<ParsedMetadata<P>, P::Error> {
|
||||||
|
let data = P::parse(input)?;
|
||||||
|
Ok(ParsedMetadata::new(P::format_name(), data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Implémentation pour DIDLLite =============
|
||||||
|
|
||||||
|
impl MediaMetadataParser for DIDLLite {
|
||||||
|
type Error = quick_xml::de::DeError;
|
||||||
|
|
||||||
|
fn parse(input: &str) -> Result<Self, Self::Error> {
|
||||||
|
quick_xml::de::from_str(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_name() -> &'static str {
|
||||||
|
"DIDL-Lite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias pour faciliter l'utilisation
|
||||||
|
pub type DidlMetadata = ParsedMetadata<DIDLLite>;
|
||||||
|
|
||||||
|
// ============= Structures DIDL-Lite =============
|
||||||
|
|
||||||
|
|
||||||
|
/// Racine d'un document DIDL-Lite
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, Reflect)]
|
||||||
|
#[serde(rename = "DIDL-Lite")]
|
||||||
|
pub struct DIDLLite {
|
||||||
|
#[serde(rename = "@xmlns")]
|
||||||
|
pub xmlns: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@xmlns:upnp", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub xmlns_upnp: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@xmlns:dc", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub xmlns_dc: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@xmlns:dlna", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub xmlns_dlna: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@xmlns:sec", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub xmlns_sec: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@xmlns:pv", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub xmlns_pv: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "container", default)]
|
||||||
|
pub containers: Vec<Container>,
|
||||||
|
|
||||||
|
#[serde(rename = "item", default)]
|
||||||
|
pub items: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Container pouvant contenir d'autres containers ou items
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, Reflect)]
|
||||||
|
pub struct Container {
|
||||||
|
#[serde(rename = "@id")]
|
||||||
|
pub id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@parentID")]
|
||||||
|
pub parent_id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@restricted", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub restricted: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@childCount", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub child_count: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "dc:title", alias = "title")]
|
||||||
|
pub title: String,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:class", alias = "class")]
|
||||||
|
pub class: String,
|
||||||
|
|
||||||
|
#[serde(rename = "container", default)]
|
||||||
|
pub containers: Vec<Container>,
|
||||||
|
|
||||||
|
#[serde(rename = "item", default)]
|
||||||
|
pub items: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Item représentant un objet audio
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, Reflect)]
|
||||||
|
pub struct Item {
|
||||||
|
#[serde(rename = "@id")]
|
||||||
|
pub id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@parentID")]
|
||||||
|
pub parent_id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@restricted", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub restricted: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "dc:title", alias = "title")]
|
||||||
|
pub title: String,
|
||||||
|
|
||||||
|
#[serde(rename = "dc:creator", alias = "creator", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub creator: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:class", alias = "class")]
|
||||||
|
pub class: String,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:artist", alias = "artist", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub artist: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:album", alias = "album", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub album: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:genre", alias = "genre", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub genre: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:albumArtURI", alias = "albumArtURI", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub album_art: Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub album_art_pk: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "dc:date", alias = "date", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub date: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "upnp:originalTrackNumber", alias = "originalTrackNumber", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub original_track_number: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "res", default)]
|
||||||
|
pub resources: Vec<Resource>,
|
||||||
|
|
||||||
|
#[serde(rename = "desc", default)]
|
||||||
|
pub descriptions: Vec<Description>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ressource média (fichier audio)
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, Reflect)]
|
||||||
|
pub struct Resource {
|
||||||
|
#[serde(rename = "@protocolInfo")]
|
||||||
|
pub protocol_info: String,
|
||||||
|
|
||||||
|
#[serde(rename = "@bitsPerSample", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bits_per_sample: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@sampleFrequency", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub sample_frequency: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@nrAudioChannels", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nr_audio_channels: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@duration", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub duration: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "$text")]
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Description avec métadonnées additionnelles (replaygain, etc.)
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, Reflect)]
|
||||||
|
pub struct Description {
|
||||||
|
#[serde(rename = "@id", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub id: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "@nameSpace", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub namespace: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "track_gain", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub track_gain: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "track_peak", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub track_peak: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Implémentation des méthodes =============
|
||||||
|
|
||||||
|
impl DIDLLite {
|
||||||
|
/// Itère sur tous les containers de manière récursive
|
||||||
|
pub fn all_containers(&self) -> impl Iterator<Item = &Container> {
|
||||||
|
AllContainersIter::new(&self.containers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Itère sur tous les items de manière récursive
|
||||||
|
pub fn all_items(&self) -> impl Iterator<Item = &Item> {
|
||||||
|
AllItemsIter::new(&self.containers, &self.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trouve un container par ID
|
||||||
|
pub fn get_container_by_id(&self, id: &str) -> Option<&Container> {
|
||||||
|
self.all_containers().find(|c| c.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trouve un item par ID
|
||||||
|
pub fn get_item_by_id(&self, id: &str) -> Option<&Item> {
|
||||||
|
self.all_items().find(|i| i.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filtre les containers
|
||||||
|
pub fn filter_containers<F>(&self, predicate: F) -> impl Iterator<Item = &Container>
|
||||||
|
where
|
||||||
|
F: Fn(&Container) -> bool,
|
||||||
|
{
|
||||||
|
self.all_containers().filter(move |c| predicate(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filtre les items
|
||||||
|
pub fn filter_items<F>(&self, predicate: F) -> impl Iterator<Item = &Item>
|
||||||
|
where
|
||||||
|
F: Fn(&Item) -> bool,
|
||||||
|
{
|
||||||
|
self.all_items().filter(move |i| predicate(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Génère une représentation Markdown
|
||||||
|
pub fn to_markdown(&self) -> String {
|
||||||
|
let mut buf = String::new();
|
||||||
|
buf.push_str("### DIDL-Lite Document\n\n");
|
||||||
|
|
||||||
|
if !self.containers.is_empty() {
|
||||||
|
buf.push_str("#### Containers\n\n");
|
||||||
|
for container in &self.containers {
|
||||||
|
container.write_markdown(&mut buf, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.items.is_empty() {
|
||||||
|
buf.push_str("#### Items\n\n");
|
||||||
|
for item in &self.items {
|
||||||
|
item.write_markdown(&mut buf, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Container {
|
||||||
|
/// Itère sur tous les containers enfants récursivement
|
||||||
|
pub fn all_containers(&self) -> impl Iterator<Item = &Container> {
|
||||||
|
AllContainersIter::new(&self.containers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Itère sur tous les items de ce container et ses enfants
|
||||||
|
pub fn all_items(&self) -> impl Iterator<Item = &Item> {
|
||||||
|
AllItemsIter::new(&self.containers, &self.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_markdown(&self, buf: &mut String, depth: usize) {
|
||||||
|
let indent = " ".repeat(depth);
|
||||||
|
|
||||||
|
writeln!(buf, "{}- **Container**: {}", indent, self.title).unwrap();
|
||||||
|
writeln!(buf, "{} - ID: `{}`", indent, self.id).unwrap();
|
||||||
|
writeln!(buf, "{} - ParentID: `{}`", indent, self.parent_id).unwrap();
|
||||||
|
writeln!(buf, "{} - Class: `{}`", indent, self.class).unwrap();
|
||||||
|
|
||||||
|
if let Some(ref restricted) = self.restricted {
|
||||||
|
writeln!(buf, "{} - Restricted: `{}`", indent, restricted).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref count) = self.child_count {
|
||||||
|
writeln!(buf, "{} - ChildCount: `{}`", indent, count).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.containers.is_empty() {
|
||||||
|
writeln!(buf, "{} - Subcontainers:", indent).unwrap();
|
||||||
|
for sub in &self.containers {
|
||||||
|
sub.write_markdown(buf, depth + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.items.is_empty() {
|
||||||
|
writeln!(buf, "{} - Items:", indent).unwrap();
|
||||||
|
for item in &self.items {
|
||||||
|
item.write_markdown(buf, depth + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
/// Itère sur les ressources audio uniquement
|
||||||
|
pub fn audio_resources(&self) -> impl Iterator<Item = &Resource> {
|
||||||
|
self.resources.iter()
|
||||||
|
.filter(|r| r.protocol_info.contains("audio/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retourne la ressource principale (première disponible)
|
||||||
|
pub fn primary_resource(&self) -> Option<&Resource> {
|
||||||
|
self.resources.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Itère sur les métadonnées sous forme de paires clé-valeur
|
||||||
|
pub fn metadata(&self) -> impl Iterator<Item = (&str, &str)> {
|
||||||
|
let mut pairs = Vec::new();
|
||||||
|
|
||||||
|
pairs.push(("title", self.title.as_str()));
|
||||||
|
|
||||||
|
if let Some(ref artist) = self.artist {
|
||||||
|
pairs.push(("artist", artist.as_str()));
|
||||||
|
}
|
||||||
|
if let Some(ref album) = self.album {
|
||||||
|
pairs.push(("album", album.as_str()));
|
||||||
|
}
|
||||||
|
if let Some(ref genre) = self.genre {
|
||||||
|
pairs.push(("genre", genre.as_str()));
|
||||||
|
}
|
||||||
|
if let Some(ref date) = self.date {
|
||||||
|
pairs.push(("date", date.as_str()));
|
||||||
|
}
|
||||||
|
if let Some(ref track) = self.original_track_number {
|
||||||
|
pairs.push(("trackNumber", track.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for desc in &self.descriptions {
|
||||||
|
if let Some(ref gain) = desc.track_gain {
|
||||||
|
pairs.push(("replayGain", gain.as_str()));
|
||||||
|
}
|
||||||
|
if let Some(ref peak) = desc.track_peak {
|
||||||
|
pairs.push(("replayPeak", peak.as_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_markdown(&self, buf: &mut String, depth: usize) {
|
||||||
|
let indent = " ".repeat(depth);
|
||||||
|
|
||||||
|
writeln!(buf, "{}- **Item**: {}", indent, self.title).unwrap();
|
||||||
|
writeln!(buf, "{} - ID: `{}`", indent, self.id).unwrap();
|
||||||
|
writeln!(buf, "{} - ParentID: `{}`", indent, self.parent_id).unwrap();
|
||||||
|
writeln!(buf, "{} - Class: `{}`", indent, self.class).unwrap();
|
||||||
|
|
||||||
|
if let Some(ref creator) = self.creator {
|
||||||
|
writeln!(buf, "{} - Creator: {}", indent, creator).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref artist) = self.artist {
|
||||||
|
writeln!(buf, "{} - Artist: {}", indent, artist).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref album) = self.album {
|
||||||
|
writeln!(buf, "{} - Album: {}", indent, album).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref genre) = self.genre {
|
||||||
|
writeln!(buf, "{} - Genre: {}", indent, genre).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref art) = self.album_art {
|
||||||
|
writeln!(buf, "{} - Album Art: ", indent, art).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref date) = self.date {
|
||||||
|
writeln!(buf, "{} - Date: {}", indent, date).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref track) = self.original_track_number {
|
||||||
|
writeln!(buf, "{} - Track: {}", indent, track).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.resources.is_empty() {
|
||||||
|
writeln!(buf, "{} - Resources:", indent).unwrap();
|
||||||
|
for res in &self.resources {
|
||||||
|
writeln!(buf, "{} - URL: {}", indent, res.url).unwrap();
|
||||||
|
writeln!(buf, "{} - Protocol: `{}`", indent, res.protocol_info).unwrap();
|
||||||
|
if let Some(ref dur) = res.duration {
|
||||||
|
writeln!(buf, "{} - Duration: `{}`", indent, dur).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref bits) = res.bits_per_sample {
|
||||||
|
writeln!(buf, "{} - BitsPerSample: `{}`", indent, bits).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref freq) = res.sample_frequency {
|
||||||
|
writeln!(buf, "{} - SampleFrequency: `{}`", indent, freq).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref channels) = res.nr_audio_channels {
|
||||||
|
writeln!(buf, "{} - Channels: `{}`", indent, channels).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.descriptions.is_empty() {
|
||||||
|
writeln!(buf, "{} - Descriptions:", indent).unwrap();
|
||||||
|
for desc in &self.descriptions {
|
||||||
|
if let Some(ref ns) = desc.namespace {
|
||||||
|
writeln!(buf, "{} - Namespace: `{}`", indent, ns).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref gain) = desc.track_gain {
|
||||||
|
writeln!(buf, "{} - Track Gain: `{}`", indent, gain).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(ref peak) = desc.track_peak {
|
||||||
|
writeln!(buf, "{} - Track Peak: `{}`", indent, peak).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Itérateurs personnalisés =============
|
||||||
|
|
||||||
|
struct AllContainersIter<'a> {
|
||||||
|
stack: Vec<&'a Container>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AllContainersIter<'a> {
|
||||||
|
fn new(containers: &'a [Container]) -> Self {
|
||||||
|
Self {
|
||||||
|
stack: containers.iter().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for AllContainersIter<'a> {
|
||||||
|
type Item = &'a Container;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.stack.pop().map(|container| {
|
||||||
|
// Ajouter les enfants à la pile
|
||||||
|
self.stack.extend(container.containers.iter());
|
||||||
|
container
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AllItemsIter<'a> {
|
||||||
|
containers: Vec<&'a Container>,
|
||||||
|
current_items: std::slice::Iter<'a, Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AllItemsIter<'a> {
|
||||||
|
fn new(containers: &'a [Container], items: &'a [Item]) -> Self {
|
||||||
|
Self {
|
||||||
|
containers: containers.iter().collect(),
|
||||||
|
current_items: items.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for AllItemsIter<'a> {
|
||||||
|
type Item = &'a Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
if let Some(item) = self.current_items.next() {
|
||||||
|
return Some(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = self.containers.pop()?;
|
||||||
|
self.containers.extend(container.containers.iter());
|
||||||
|
self.current_items = container.items.iter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_simple_didl() {
|
||||||
|
let xml = r#"
|
||||||
|
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">
|
||||||
|
<item id="1" parentID="0">
|
||||||
|
<dc:title>Test Song</dc:title>
|
||||||
|
<upnp:class>object.item.audioItem.musicTrack</upnp:class>
|
||||||
|
<res protocolInfo="http-get:*:audio/mpeg:*">http://example.com/song.mp3</res>
|
||||||
|
</item>
|
||||||
|
</DIDL-Lite>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let didl = DIDLLite::parse(xml).unwrap();
|
||||||
|
assert_eq!(didl.items.len(), 1);
|
||||||
|
assert_eq!(didl.items[0].title, "Test Song");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_without_namespaces() {
|
||||||
|
// Teste un XML sans namespaces explicites (devices UPnP laxistes)
|
||||||
|
let xml = r#"
|
||||||
|
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
|
||||||
|
<item id="1" parentID="0">
|
||||||
|
<title>Test Song</title>
|
||||||
|
<class>object.item.audioItem.musicTrack</class>
|
||||||
|
<res protocolInfo="http-get:*:audio/mpeg:*">http://example.com/song.mp3</res>
|
||||||
|
</item>
|
||||||
|
</DIDL-Lite>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let didl = DIDLLite::parse(xml).unwrap();
|
||||||
|
assert_eq!(didl.items.len(), 1);
|
||||||
|
assert_eq!(didl.items[0].title, "Test Song");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generic_parser() {
|
||||||
|
let xml = r#"
|
||||||
|
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">
|
||||||
|
</DIDL-Lite>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Utiliser le parser générique
|
||||||
|
let metadata: DidlMetadata = parse_metadata(xml).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(metadata.format, "DIDL-Lite");
|
||||||
|
assert!(metadata.parsed_at.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_metadata_map() {
|
||||||
|
let xml = r#"
|
||||||
|
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">
|
||||||
|
</DIDL-Lite>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let metadata: DidlMetadata = parse_metadata(xml).unwrap();
|
||||||
|
|
||||||
|
// Transformer les données
|
||||||
|
let item_count = metadata.map(|didl| didl.items.len());
|
||||||
|
|
||||||
|
assert_eq!(item_count.format, "DIDL-Lite");
|
||||||
|
assert_eq!(item_count.data, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pmoconfig = { path = "../pmoconfig" }
|
pmoconfig = { path = "../pmoconfig" }
|
||||||
|
pmodidl = { path = "../pmodidl"}
|
||||||
|
|
||||||
url = "2.5.7"
|
url = "2.5.7"
|
||||||
uuid = "1.18.1"
|
uuid = "1.18.1"
|
||||||
@@ -34,3 +35,5 @@ anyhow = "1.0"
|
|||||||
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||||
validator = { version = "0.20.0", features = ["derive"] }
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
|
bevy_reflect = "0.17.1"
|
||||||
|
bevy_reflect_derive = "0.17.1"
|
||||||
|
|||||||
197
pmoupnp/src/actions/macros.rs
Normal file
197
pmoupnp/src/actions/macros.rs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/// Macro pour définir facilement des actions UPnP
|
||||||
|
///
|
||||||
|
/// # Syntaxe
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// define_action! {
|
||||||
|
/// pub static ACTION_NAME = "ActionName" {
|
||||||
|
/// in "ParamName" => VARIABLE_REF,
|
||||||
|
/// in "OtherParam" => OTHER_VAR,
|
||||||
|
/// out "ResultParam" => RESULT_VAR,
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_action {
|
||||||
|
// Variante sans arguments
|
||||||
|
(pub static $name:ident = $action_name:literal) => {
|
||||||
|
pub static $name: once_cell::sync::Lazy<$crate::actions::Action> =
|
||||||
|
once_cell::sync::Lazy::new(|| {
|
||||||
|
$crate::actions::Action::new($action_name.to_string())
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Variante avec arguments
|
||||||
|
(pub static $name:ident = $action_name:literal {
|
||||||
|
$(
|
||||||
|
$direction:ident $arg_name:literal => $var_ref:expr
|
||||||
|
),* $(,)?
|
||||||
|
}) => {
|
||||||
|
pub static $name: once_cell::sync::Lazy<$crate::actions::Action> =
|
||||||
|
once_cell::sync::Lazy::new(|| {
|
||||||
|
let mut ac = $crate::actions::Action::new($action_name.to_string());
|
||||||
|
|
||||||
|
$(
|
||||||
|
ac.add_argument(
|
||||||
|
define_action!(@arg $direction $arg_name, $var_ref)
|
||||||
|
);
|
||||||
|
)*
|
||||||
|
|
||||||
|
ac
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helpers internes pour créer les arguments
|
||||||
|
(@arg in $name:literal, $var:expr) => {
|
||||||
|
$crate::actions::Argument::new_in($name.to_string(), $var.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
(@arg out $name:literal, $var:expr) => {
|
||||||
|
$crate::actions::Argument::new_out($name.to_string(), $var.clone())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Exemples d'utilisation =============
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod examples {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Utilisation originale (pour comparaison)
|
||||||
|
mod original {
|
||||||
|
use crate::mediarenderer::avtransport::variables::{
|
||||||
|
A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
TRANSPORTPLAYSPEED
|
||||||
|
};
|
||||||
|
use crate::actions::{Action, Argument};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static PLAY: Lazy<Action> = Lazy::new(|| -> Action {
|
||||||
|
let mut ac = Action::new("Play".to_string());
|
||||||
|
|
||||||
|
ac.add_argument(
|
||||||
|
Argument::new_in("InstanceID".to_string(),
|
||||||
|
A_ARG_TYPE_INSTANCE_ID.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
ac.add_argument(
|
||||||
|
Argument::new_in("Speed".to_string(),
|
||||||
|
TRANSPORTPLAYSPEED.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
ac
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avec la macro - Version simple
|
||||||
|
mod with_macro {
|
||||||
|
use crate::mediarenderer::avtransport::variables::*;
|
||||||
|
|
||||||
|
define_action! {
|
||||||
|
pub static PLAY = "Play" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
in "Speed" => TRANSPORTPLAYSPEED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_action! {
|
||||||
|
pub static SETAVTRANSPORTURI = "SetAVTransportURI" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
in "CurrentURI" => AVTRANSPORTURI,
|
||||||
|
in "CurrentURIMetaData" => AVTRANSPORTURIMETADATA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_action! {
|
||||||
|
pub static STOP = "Stop" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action sans arguments
|
||||||
|
define_action! {
|
||||||
|
pub static PAUSE = "Pause"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Macro alternative encore plus concise =============
|
||||||
|
|
||||||
|
/// Macro pour définir plusieurs actions en une fois
|
||||||
|
///
|
||||||
|
/// # Syntaxe
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// define_actions! {
|
||||||
|
/// PLAY = "Play" {
|
||||||
|
/// in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
/// in "Speed" => TRANSPORTPLAYSPEED,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// STOP = "Stop" {
|
||||||
|
/// in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_actions {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$name:ident = $action_name:literal {
|
||||||
|
$(
|
||||||
|
$direction:ident $arg_name:literal => $var_ref:expr
|
||||||
|
),* $(,)?
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
define_action! {
|
||||||
|
pub static $name = $action_name {
|
||||||
|
$($direction $arg_name => $var_ref),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
|
||||||
|
// Support pour actions sans arguments
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$name:ident = $action_name:literal
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
define_action! {
|
||||||
|
pub static $name = $action_name
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= Exemple d'utilisation groupée =============
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod grouped_example {
|
||||||
|
use crate::mediarenderer::avtransport::variables::*;
|
||||||
|
|
||||||
|
define_actions! {
|
||||||
|
PLAY = "Play" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
in "Speed" => TRANSPORTPLAYSPEED,
|
||||||
|
}
|
||||||
|
|
||||||
|
STOP = "Stop" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
PAUSE = "Pause" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
SETAVTRANSPORTURI = "SetAVTransportURI" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
in "CurrentURI" => AVTRANSPORTURI,
|
||||||
|
in "CurrentURIMetaData" => AVTRANSPORTURIMETADATA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,15 @@ mod action_instance_set;
|
|||||||
mod argument;
|
mod argument;
|
||||||
mod arg_set_methods;
|
mod arg_set_methods;
|
||||||
|
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::{state_variables::StateVariable, UpnpObjectType};
|
use crate::{state_variables::StateVariable, UpnpObjectType};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Action {
|
pub struct Action {
|
||||||
object: UpnpObjectType,
|
object: UpnpObjectType,
|
||||||
arguments: ArgumentSet,
|
arguments: ArgumentSet,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ pub mod value_ranges;
|
|||||||
pub mod variable_types;
|
pub mod variable_types;
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
|
|
||||||
|
pub mod mediarenderer;
|
||||||
|
|
||||||
pub use crate::object_trait::{UpnpXml,UpnpObject};
|
pub use crate::object_trait::{UpnpXml,UpnpObject};
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
|
|||||||
8
pmoupnp/src/mediarenderer/avtransport/actions/mod.rs
Normal file
8
pmoupnp/src/mediarenderer/avtransport/actions/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod play;
|
||||||
|
mod stop;
|
||||||
|
mod setavtransporturi;
|
||||||
|
|
||||||
|
pub use play::PLAY;
|
||||||
|
pub use stop::STOP;
|
||||||
|
pub use setavtransporturi::SETAVTRANSPORTURI;
|
||||||
|
|
||||||
19
pmoupnp/src/mediarenderer/avtransport/actions/play.rs
Normal file
19
pmoupnp/src/mediarenderer/avtransport/actions/play.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::mediarenderer::avtransport::variables::{A_ARG_TYPE_INSTANCE_ID, TRANSPORTPLAYSPEED};
|
||||||
|
use crate::actions::{Action, Argument};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static PLAY: Lazy<Action> = Lazy::new(|| -> Action {
|
||||||
|
let mut ac = Action::new("Play".to_string());
|
||||||
|
|
||||||
|
ac.add_argument(
|
||||||
|
Argument::new_in("InstanceID".to_string(),
|
||||||
|
A_ARG_TYPE_INSTANCE_ID.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
ac.add_argument(
|
||||||
|
Argument::new_in("Speed".to_string(),
|
||||||
|
TRANSPORTPLAYSPEED.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
ac
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use crate::mediarenderer::avtransport::variables::{A_ARG_TYPE_INSTANCE_ID, AVTRANSPORTURI, AVTRANSPORTURIMETADATA};
|
||||||
|
use crate::define_action;
|
||||||
|
|
||||||
|
define_action! {
|
||||||
|
pub static SETAVTRANSPORTURI = "SetAVTransportURI" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
in "CurrentURI" => AVTRANSPORTURI,
|
||||||
|
in "CurrentURIMetaData" => AVTRANSPORTURIMETADATA,
|
||||||
|
}
|
||||||
|
}
|
||||||
8
pmoupnp/src/mediarenderer/avtransport/actions/stop.rs
Normal file
8
pmoupnp/src/mediarenderer/avtransport/actions/stop.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::mediarenderer::avtransport::variables::A_ARG_TYPE_INSTANCE_ID;
|
||||||
|
use crate::define_action;
|
||||||
|
|
||||||
|
define_action! {
|
||||||
|
pub static STOP = "Stop" {
|
||||||
|
in "InstanceID" => A_ARG_TYPE_INSTANCE_ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
3
pmoupnp/src/mediarenderer/avtransport/mod.rs
Normal file
3
pmoupnp/src/mediarenderer/avtransport/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod variables;
|
||||||
|
pub mod actions;
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static A_ARG_TYPE_INSTANCE_ID: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
StateVariable::new(StateVarType::UI4, "A_ARG_TYPE_InstanceID".to_string())
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static A_ARG_TYPE_PLAY_SPEED: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
StateVariable::new(StateVarType::String, "A_ARG_TYPE_PlaySpeed".to_string())
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static AVTRANSPORTURI: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
StateVariable::new(StateVarType::String, "AVTransportURI".to_string())
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::state_variables::{StateVariable, StateVariableError};
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use bevy_reflect::Reflect;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use pmodidl::{DIDLLite, MediaMetadataParser};
|
||||||
|
|
||||||
|
// func _AVTransportURIMetaDataParser(value string) (interface{}, error) {
|
||||||
|
// log.Debug("[avtransport] Parsing AVTransport)")
|
||||||
|
// didl, err := pmodidl.Parse(value)
|
||||||
|
// if err != nil {
|
||||||
|
// return value, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return didl, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn avtransporturimetadataparser(value: &str) -> Result<Box<dyn Reflect>, StateVariableError> {
|
||||||
|
// Parse DIDL-Lite
|
||||||
|
let didl = DIDLLite::parse(value)
|
||||||
|
.map_err(|e| StateVariableError::ParseError(format!("Failed to parse DIDL-Lite: {}", e)))?;
|
||||||
|
|
||||||
|
// Retourne le résultat sous forme de Box<dyn Reflect>
|
||||||
|
Ok(Box::new(didl) as Box<dyn Reflect>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static AVTRANSPORTURIMETADATA: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
let mut sv = StateVariable::new(StateVarType::String, "AVTransportURIMetaData".to_string());
|
||||||
|
|
||||||
|
sv.set_value_parser(Arc::new(avtransporturimetadataparser)).expect("Failed to set parser");
|
||||||
|
sv
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static CURRENTTRACKDURATION: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
StateVariable::new(StateVarType::String, "CurrentTrackDuration".to_string())
|
||||||
|
});
|
||||||
|
|
||||||
22
pmoupnp/src/mediarenderer/avtransport/variables/mod.rs
Normal file
22
pmoupnp/src/mediarenderer/avtransport/variables/mod.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
mod a_arg_type_instanceid;
|
||||||
|
mod a_arg_type_playspeed;
|
||||||
|
mod avtransporturi;
|
||||||
|
mod avtransporturimetadata;
|
||||||
|
mod currenttrackduration;
|
||||||
|
mod seekmode;
|
||||||
|
mod transportplayspeed;
|
||||||
|
mod transportstate;
|
||||||
|
mod transportstatus;
|
||||||
|
|
||||||
|
pub use a_arg_type_instanceid::A_ARG_TYPE_INSTANCE_ID;
|
||||||
|
pub use a_arg_type_playspeed::A_ARG_TYPE_PLAY_SPEED;
|
||||||
|
pub use avtransporturi::AVTRANSPORTURI;
|
||||||
|
pub use avtransporturimetadata::AVTRANSPORTURIMETADATA;
|
||||||
|
pub use currenttrackduration::CURRENTTRACKDURATION;
|
||||||
|
pub use seekmode::SEEKMODE;
|
||||||
|
pub use transportplayspeed::TRANSPORTPLAYSPEED;
|
||||||
|
pub use transportstate::TRANSPORTSTATE;
|
||||||
|
pub use transportstatus::TRANSPORTSTATUS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::StateVarType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static SEEKMODE: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
StateVariable::new(StateVarType::String, "SeekMode".to_string())
|
||||||
|
});
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::{StateValue, StateVarType};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static TRANSPORTPLAYSPEED: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
let mut sv = StateVariable::new(StateVarType::String, "TransportPlaySpeed".to_string());
|
||||||
|
|
||||||
|
sv.push_allowed_value(&StateValue::String("1".to_string())).expect("Cannot add allowed value");
|
||||||
|
sv.set_default(&StateValue::String("1".to_string())).expect("Cannt set default value");
|
||||||
|
|
||||||
|
sv
|
||||||
|
});
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::{StateValue, StateVarType};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static TRANSPORTSTATE: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
let mut sv = StateVariable::new(StateVarType::String, "TransportState".to_string());
|
||||||
|
|
||||||
|
sv.push_allowed_value(&StateValue::String("NO_MEDIA_PRESENT".to_string())).expect("Cannot add allowed value");
|
||||||
|
sv.extend_allowed_values(&[
|
||||||
|
StateValue::String("STOPPED".to_string()),
|
||||||
|
StateValue::String("PLAYING".to_string()),
|
||||||
|
StateValue::String("RECORDING".to_string()),
|
||||||
|
StateValue::String("TRANSITIONING".to_string()),
|
||||||
|
StateValue::String("PAUSED_PLAYBACK".to_string()),
|
||||||
|
StateValue::String("PAUSED_RECORDING".to_string()),
|
||||||
|
StateValue::String("NO_MEDIA_PRESENT".to_string()),
|
||||||
|
]).expect("Cannt set default value");
|
||||||
|
|
||||||
|
sv
|
||||||
|
});
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::state_variables::StateVariable;
|
||||||
|
use crate::variable_types::{StateValue, StateVarType};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static TRANSPORTSTATUS: Lazy<StateVariable> = Lazy::new(|| -> StateVariable {
|
||||||
|
let mut sv = StateVariable::new(StateVarType::String, "TransportStatus".to_string());
|
||||||
|
|
||||||
|
sv.push_allowed_value(&StateValue::String("OK".to_string()))
|
||||||
|
.expect("Cannot add allowed value");
|
||||||
|
sv.extend_allowed_values(&[
|
||||||
|
StateValue::String("OK".to_string()),
|
||||||
|
StateValue::String("ERROR_OCCURRED".to_string()),
|
||||||
|
])
|
||||||
|
.expect("Cannt set default value");
|
||||||
|
|
||||||
|
sv
|
||||||
|
});
|
||||||
1
pmoupnp/src/mediarenderer/mod.rs
Normal file
1
pmoupnp/src/mediarenderer/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod avtransport;
|
||||||
@@ -24,8 +24,72 @@ pub trait UpnpXml {
|
|||||||
|
|
||||||
xml_string
|
xml_string
|
||||||
}
|
}
|
||||||
|
fn to_markdown(&self) -> String {
|
||||||
|
let elem = self.to_xml_element();
|
||||||
|
let mut md = String::new();
|
||||||
|
|
||||||
|
fn is_url(s: &str) -> bool {
|
||||||
|
s.starts_with("http://") || s.starts_with("https://") || s.starts_with("urn:")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_image_url(s: &str) -> bool {
|
||||||
|
let s = s.to_lowercase();
|
||||||
|
s.ends_with(".png")
|
||||||
|
|| s.ends_with(".jpg")
|
||||||
|
|| s.ends_with(".jpeg")
|
||||||
|
|| s.ends_with(".gif")
|
||||||
|
|| s.ends_with(".svg")
|
||||||
|
|| s.ends_with(".webp")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_value(v: &str) -> String {
|
||||||
|
let v = v.trim().to_string(); // <-- clone du texte nettoyé
|
||||||
|
if is_url(&v) {
|
||||||
|
if is_image_url(&v) {
|
||||||
|
format!("[{}]({})<br>", v, v, v)
|
||||||
|
} else {
|
||||||
|
format!("[{}]({})", v, v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
format!("`{}`", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recurse(elem: &xmltree::Element, md: &mut String, depth: usize) {
|
||||||
|
let indent = " ".repeat(depth);
|
||||||
|
md.push_str(&format!("{}- **{}**", indent, elem.name));
|
||||||
|
|
||||||
|
// Attributs
|
||||||
|
if !elem.attributes.is_empty() {
|
||||||
|
let attrs: Vec<String> = elem
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}={}", k, format_value(v)))
|
||||||
|
.collect();
|
||||||
|
md.push_str(&format!(" ({})", attrs.join(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texte (utilise get_text() maintenant)
|
||||||
|
if let Some(text) = elem.get_text().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()) {
|
||||||
|
md.push_str(&format!(": {}", format_value(&text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
md.push('\n');
|
||||||
|
|
||||||
|
// Enfants
|
||||||
|
for child in &elem.children {
|
||||||
|
if let xmltree::XMLNode::Element(child_elem) = child {
|
||||||
|
recurse(child_elem, md, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md.push_str("# UPnP XML (Markdown view)\n\n");
|
||||||
|
recurse(&elem, &mut md, 0);
|
||||||
|
md
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub trait UpnpObject : UpnpXml {
|
pub trait UpnpObject: UpnpXml {
|
||||||
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
||||||
|
|
||||||
fn get_name(&self) -> &String {
|
fn get_name(&self) -> &String {
|
||||||
@@ -35,6 +99,4 @@ pub trait UpnpObject : UpnpXml {
|
|||||||
fn get_object_type(&self) -> &String {
|
fn get_object_type(&self) -> &String {
|
||||||
&self.as_upnp_object_type().object_type
|
&self.as_upnp_object_type().object_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use rust_embed::RustEmbed;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{net::SocketAddr, sync::Arc};
|
use std::{net::SocketAddr, sync::Arc};
|
||||||
use tokio::{signal, sync::RwLock, task::JoinHandle};
|
use tokio::{signal, sync::RwLock, task::JoinHandle};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{info, warn, debug, error};
|
||||||
use utoipa::OpenApi;
|
use utoipa::OpenApi;
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
@@ -150,16 +150,17 @@ impl Server {
|
|||||||
///
|
///
|
||||||
/// # Exemple
|
/// # Exemple
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```ignore
|
||||||
/// # use pmoupnp::server::Server;
|
/// use pmoupnp::server::Server;
|
||||||
/// # use rust_embed::RustEmbed;
|
/// use rust_embed::RustEmbed;
|
||||||
|
///
|
||||||
/// #[derive(RustEmbed, Clone)]
|
/// #[derive(RustEmbed, Clone)]
|
||||||
/// #[folder = "static/"]
|
/// #[folder = "static/"]
|
||||||
/// struct Assets;
|
/// struct Assets;
|
||||||
///
|
///
|
||||||
/// # #[tokio::main]
|
/// # #[tokio::main]
|
||||||
/// # async fn main() {
|
/// # async fn main() {
|
||||||
/// # let mut server = Server::new("Test", "http://localhost:3000", 3000);
|
/// let mut server = Server::new("Test", "http://localhost:3000", 3000);
|
||||||
/// server.add_dir::<Assets>("/assets").await;
|
/// server.add_dir::<Assets>("/assets").await;
|
||||||
/// // Les fichiers de static/ sont accessibles via /assets/*
|
/// // Les fichiers de static/ sont accessibles via /assets/*
|
||||||
/// # }
|
/// # }
|
||||||
@@ -288,17 +289,23 @@ impl Server {
|
|||||||
///
|
///
|
||||||
/// # Exemple avec SSE
|
/// # Exemple avec SSE
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```ignore
|
||||||
/// # use pmoupnp::server::Server;
|
/// use pmoupnp::server::Server;
|
||||||
/// # use axum::extract::State;
|
/// use axum::extract::State;
|
||||||
/// # use axum::response::sse::{Event, Sse, KeepAlive};
|
/// use axum::response::sse::{Event, Sse, KeepAlive};
|
||||||
/// # use tokio::sync::broadcast;
|
/// use tokio::sync::broadcast;
|
||||||
/// # #[derive(Clone)]
|
///
|
||||||
/// # struct LogState { tx: broadcast::Sender<String> }
|
/// #[derive(Clone)]
|
||||||
/// # impl LogState { fn subscribe(&self) -> broadcast::Receiver<String> { self.tx.subscribe() } }
|
/// struct LogState {
|
||||||
/// # #[tokio::main]
|
/// tx: broadcast::Sender<String>
|
||||||
/// # async fn main() {
|
/// }
|
||||||
/// # let mut server = Server::new("Test", "http://localhost:3000", 3000);
|
///
|
||||||
|
/// impl LogState {
|
||||||
|
/// fn subscribe(&self) -> broadcast::Receiver<String> {
|
||||||
|
/// self.tx.subscribe()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
/// async fn log_sse(State(state): State<LogState>) -> Sse<impl futures::Stream<Item = Result<Event, std::convert::Infallible>>> {
|
/// async fn log_sse(State(state): State<LogState>) -> Sse<impl futures::Stream<Item = Result<Event, std::convert::Infallible>>> {
|
||||||
/// let mut rx = state.subscribe();
|
/// let mut rx = state.subscribe();
|
||||||
/// let stream = async_stream::stream! {
|
/// let stream = async_stream::stream! {
|
||||||
@@ -311,7 +318,6 @@ impl Server {
|
|||||||
///
|
///
|
||||||
/// let log_state = LogState { tx: broadcast::channel(100).0 };
|
/// let log_state = LogState { tx: broadcast::channel(100).0 };
|
||||||
/// server.add_handler_with_state("/logs", log_sse, log_state).await;
|
/// server.add_handler_with_state("/logs", log_sse, log_state).await;
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn add_handler_with_state<H, T, S>(&mut self, path: &str, handler: H, state: S)
|
pub async fn add_handler_with_state<H, T, S>(&mut self, path: &str, handler: H, state: S)
|
||||||
where
|
where
|
||||||
@@ -319,7 +325,9 @@ impl Server {
|
|||||||
T: 'static,
|
T: 'static,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let route = Router::new().route("/", get(handler)).with_state(state);
|
let route = Router::new()
|
||||||
|
.route("/", get(handler))
|
||||||
|
.with_state(state);
|
||||||
|
|
||||||
let mut r = self.router.write().await;
|
let mut r = self.router.write().await;
|
||||||
*r = std::mem::take(&mut *r).nest(path, route);
|
*r = std::mem::take(&mut *r).nest(path, route);
|
||||||
@@ -398,9 +406,16 @@ impl Server {
|
|||||||
///
|
///
|
||||||
/// # Exemple
|
/// # Exemple
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```ignore
|
||||||
/// use utoipa::OpenApi;
|
/// use utoipa::OpenApi;
|
||||||
/// use axum::{Router, Json, routing::get};
|
/// use axum::{Router, Json, routing::get};
|
||||||
|
/// use serde::{Serialize, Deserialize};
|
||||||
|
///
|
||||||
|
/// #[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
||||||
|
/// struct User {
|
||||||
|
/// id: u64,
|
||||||
|
/// name: String,
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// #[derive(utoipa::OpenApi)]
|
/// #[derive(utoipa::OpenApi)]
|
||||||
/// #[openapi(
|
/// #[openapi(
|
||||||
@@ -429,7 +444,8 @@ impl Server {
|
|||||||
*api_r = Some(api_router);
|
*api_r = Some(api_router);
|
||||||
|
|
||||||
// Ajouter Swagger UI
|
// Ajouter Swagger UI
|
||||||
let swagger = SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi);
|
let swagger = SwaggerUi::new("/swagger-ui")
|
||||||
|
.url("/api-docs/openapi.json", openapi);
|
||||||
|
|
||||||
let mut r = self.router.write().await;
|
let mut r = self.router.write().await;
|
||||||
*r = std::mem::take(&mut *r).merge(swagger);
|
*r = std::mem::take(&mut *r).merge(swagger);
|
||||||
@@ -453,10 +469,7 @@ impl Server {
|
|||||||
/// ```
|
/// ```
|
||||||
pub async fn start(&mut self) {
|
pub async fn start(&mut self) {
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], self.http_port));
|
let addr = SocketAddr::from(([0, 0, 0, 0], self.http_port));
|
||||||
info!(
|
info!("Server {} running at [http://{}:{}](http://{}:{})", self.name, self.base_url, self.http_port, self.base_url, self.http_port);
|
||||||
"Server {} running at [http://{}:{}](http://{}:{})",
|
|
||||||
self.name, self.base_url, self.http_port, self.base_url, self.http_port
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merger le routeur API si présent
|
// Merger le routeur API si présent
|
||||||
let api_router = self.api_router.read().await;
|
let api_router = self.api_router.read().await;
|
||||||
@@ -532,7 +545,7 @@ impl ServerBuilder {
|
|||||||
Self {
|
Self {
|
||||||
name: "PMO-Music-Server".to_string(),
|
name: "PMO-Music-Server".to_string(),
|
||||||
base_url: config.get_base_url(),
|
base_url: config.get_base_url(),
|
||||||
http_port: config.get_http_port(),
|
http_port: config.get_http_port()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::state_variables::variable_trait::UpnpVariable;
|
pub use crate::state_variables::variable_trait::UpnpVariable;
|
||||||
|
use bevy_reflect::Reflect;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
pub use errors::StateVariableError;
|
pub use errors::StateVariableError;
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ pub type StateConditionFunc = Arc<dyn Fn(&StateVarInstance) -> bool + Send + Syn
|
|||||||
|
|
||||||
/// Type pour les fonctions de parsing de valeurs depuis des chaînes
|
/// Type pour les fonctions de parsing de valeurs depuis des chaînes
|
||||||
pub type StringValueParser =
|
pub type StringValueParser =
|
||||||
Arc<dyn Fn(&str) -> Result<StateValue, StateVariableError> + Send + Sync>;
|
Arc<dyn Fn(&str) -> Result<Box<dyn Reflect>, StateVariableError> + Send + Sync>;
|
||||||
|
|
||||||
/// Type pour les fonctions de sérialisation de valeurs vers des chaînes
|
/// Type pour les fonctions de sérialisation de valeurs vers des chaînes
|
||||||
pub type ValueSerializer =
|
pub type ValueSerializer =
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ impl StateVariable {
|
|||||||
self.description = description;
|
self.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_default(&mut self, value: StateValue) -> Result<(), StateValueError> {
|
pub fn set_default(&mut self, value: &StateValue) -> Result<(), StateValueError> {
|
||||||
if self.as_state_var_type() != value.as_state_var_type() {
|
if self.as_state_var_type() != value.as_state_var_type() {
|
||||||
return Err(StateValueError::TypeError(
|
return Err(StateValueError::TypeError(
|
||||||
"value does not have the right type".to_string(),
|
"value does not have the right type".to_string(),
|
||||||
@@ -353,7 +353,7 @@ impl StateVariable {
|
|||||||
self.send_events = false;
|
self.send_events = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_value_parseur(&mut self, parser: StringValueParser) -> Result<(), StateValueError> {
|
pub fn set_value_parser(&mut self, parser: StringValueParser) -> Result<(), StateValueError> {
|
||||||
if self.as_state_var_type() == StateVarType::String {
|
if self.as_state_var_type() == StateVarType::String {
|
||||||
self.parse = Some(parser);
|
self.parse = Some(parser);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ mod values_from_uri;
|
|||||||
mod values_from_uuid;
|
mod values_from_uuid;
|
||||||
mod values_from_vec_u8;
|
mod values_from_vec_u8;
|
||||||
|
|
||||||
|
mod values_from_str;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
|
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
|
||||||
|
|||||||
21
pmoupnp/src/variable_types/values_from_str.rs
Normal file
21
pmoupnp/src/variable_types/values_from_str.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use std::convert::TryFrom;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::variable_types::{StateValue, StateValueError};
|
||||||
|
|
||||||
|
impl TryFrom<&str> for StateValue {
|
||||||
|
type Error = StateValueError;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, StateValueError> {
|
||||||
|
Ok(StateValue::String(s.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion depuis String
|
||||||
|
impl TryFrom<String> for StateValue {
|
||||||
|
type Error = StateValueError;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, StateValueError> {
|
||||||
|
Ok(StateValue::String(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -400,6 +400,7 @@ button.active {
|
|||||||
.message {
|
.message {
|
||||||
color: #d4d4d4;
|
color: #d4d4d4;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user