diff --git a/Cargo.lock b/Cargo.lock index 09da844..5c3fb41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.26" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e53b0a3d5760cd2ba9b787ae0c6440ad18ee294ff71b05e3381c900a7d16cfd" +checksum = "79faae4620f45232f599d9bc7b290f88247a0834162c4495ab2f02d60004adfb" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -144,6 +144,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -157,7 +163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" dependencies = [ "alsa-sys", - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -178,7 +184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags 2.5.0", + "bitflags 2.6.0", "cc", "cesu8", "jni", @@ -199,7 +205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.5.0", + "bitflags 2.6.0", "cc", "cesu8", "jni", @@ -266,9 +272,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -283,6 +289,29 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -318,16 +347,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener-strategy 0.5.2", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ "async-task", "concurrent-queue", @@ -370,17 +399,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.7.0", + "polling 3.7.2", "rustix 0.38.34", "slab", "tracing", @@ -398,12 +427,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 5.3.1", + "event-listener-strategy", "pin-project-lite", ] @@ -432,17 +461,17 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "async-signal" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", @@ -472,7 +501,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -483,13 +512,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -555,7 +584,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -565,10 +594,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] -name = "backtrace" -version = "0.3.71" +name = "av1-grain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -591,7 +643,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools", @@ -604,10 +656,16 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.63", + "syn 2.0.71", "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -616,9 +674,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitstream-io" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" [[package]] name = "block" @@ -666,12 +730,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock 3.3.0", "async-task", "futures-io", "futures-lite 2.3.0", @@ -680,9 +743,9 @@ dependencies = [ [[package]] name = "built" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a6c0b39c38fd754ac338b00a88066436389c0f029da5d37d1e01091d9b7c17" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" [[package]] name = "bumpalo" @@ -698,22 +761,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369cfaf2a5bed5d8f8202073b2e093c9f508251de1551a0deb4253e4c7d80909" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -722,6 +785,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -734,9 +803,9 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "log", - "polling 3.7.0", + "polling 3.7.2", "rustix 0.38.34", "slab", "thiserror", @@ -756,9 +825,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "907d8581360765417f8f2e0e7d602733bbed60156b4465b7617243689ef9b83d" dependencies = [ "jobserver", "libc", @@ -780,6 +849,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -818,25 +897,25 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.3", + "libloading 0.8.4", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -844,9 +923,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -856,21 +935,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "claxon" @@ -998,7 +1077,7 @@ checksum = "5387f5bbc9e9e6c96436ea125afa12614cebf8ac67f49abc08c1e7a891466c90" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1142,7 +1221,7 @@ dependencies = [ "lazy_static", "proc-macro2", "regex", - "syn 2.0.63", + "syn 2.0.71", "unicode-xid", ] @@ -1154,7 +1233,7 @@ checksum = "3e1a2532e4ed4ea13031c13bc7bc0dbca4aae32df48e9d77f0d1e743179f2ea1" dependencies = [ "lazy_static", "proc-macro2", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1169,7 +1248,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1198,9 +1277,9 @@ checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1211,6 +1290,25 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1223,7 +1321,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "mio", @@ -1242,6 +1340,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1262,14 +1366,10 @@ dependencies = [ ] [[package]] -name = "ctor" -version = "0.2.8" +name = "ctor-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" -dependencies = [ - "quote", - "syn 2.0.63", -] +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" [[package]] name = "cursor-icon" @@ -1308,15 +1408,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.71", ] [[package]] @@ -1327,7 +1427,7 @@ checksum = "61bb5a1014ce6dfc2a378578509abe775a5aa06bff584a547555d9efdb81b926" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1352,7 +1452,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.3", + "libloading 0.8.4", ] [[package]] @@ -1415,9 +1515,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" @@ -1430,9 +1530,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", "serde", @@ -1440,13 +1540,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1493,46 +1593,41 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] -[[package]] -name = "event-listener" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "extended" version = "0.1.0" @@ -1565,19 +1660,19 @@ dependencies = [ [[package]] name = "femtovg" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a056992c3a862ef9d4452bad8b6b455780382502b38b93540fb17773a54ca092" +checksum = "47921d14afc4daad9bedc926099bc6edcaa23e37a957448f86cdefcbafe2f632" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "fnv", "glow", - "image", + "image 0.25.1", "imgref", "log", "lru", "rgb", - "rustybuzz", + "rustybuzz 0.14.1", "slotmap", "unicode-bidi", "unicode-segmentation", @@ -1638,6 +1733,15 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1695,7 +1799,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1761,7 +1865,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1857,10 +1961,20 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.28.1" +name = "gif" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gl_generator" @@ -1897,7 +2011,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg_aliases 0.1.1", "cgl", "core-foundation", @@ -1906,7 +2020,7 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "icrate 0.0.4", - "libloading 0.8.3", + "libloading 0.8.4", "objc2 0.4.1", "once_cell", "raw-window-handle 0.5.2", @@ -1956,6 +2070,16 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hash32" version = "0.3.1" @@ -2003,6 +2127,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2138,7 +2268,7 @@ dependencies = [ "cfg-if", "derive_more", "fontdb", - "libloading 0.8.3", + "libloading 0.8.4", ] [[package]] @@ -2175,7 +2305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e40b1c61efe0fc5fbccdb68b23381d312ff6bf0a5cbdd72a6c7e9a669277240d" dependencies = [ "auto_enums", - "bitflags 2.5.0", + "bitflags 2.6.0", "bytemuck", "cfg-if", "clru", @@ -2185,7 +2315,7 @@ dependencies = [ "fontdue", "i-slint-common", "i-slint-core-macros", - "image", + "image 0.24.9", "integer-sqrt", "lyon_algorithms", "lyon_extra", @@ -2199,7 +2329,7 @@ dependencies = [ "raw-window-handle 0.6.2", "resvg", "rgb", - "rustybuzz", + "rustybuzz 0.13.0", "scoped-tls-hkt", "scopeguard", "slab", @@ -2221,7 +2351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12bed18eb2f3910f85ce4a4bb4c0d770147e76c072a48c845d86ad59c169c24b" dependencies = [ "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -2358,6 +2488,39 @@ dependencies = [ "png", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", +] + [[package]] name = "imagesize" version = "0.12.0" @@ -2418,13 +2581,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -2452,9 +2626,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "java-locator" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90003f2fd9c52f212c21d8520f1128da0080bad6fff16b68fe6e7f2f0c3780c2" +checksum = "d2abecabd9961c5e01405a6426687fcf1bd94a269927137e4c3cc1a7419b93fd" dependencies = [ "glob", "lazy_static", @@ -2532,9 +2706,9 @@ checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -2543,10 +2717,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] -name = "libc" -version = "0.2.154" +name = "lebe" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] [[package]] name = "libloading" @@ -2560,12 +2751,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2580,7 +2771,7 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", "redox_syscall 0.4.1", ] @@ -2664,24 +2855,26 @@ checksum = "764b60e1ddd07e5665a6a17636a95cd7d8f3b86c73503a69c32979d05f72f3cf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lonelyradio" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-stream", + "cc", "chrono", "clap", "flacenc", "futures-util", + "image 0.25.1", "lofty", "lonelyradio_types", "once_cell", @@ -2697,9 +2890,19 @@ dependencies = [ [[package]] name = "lonelyradio_types" -version = "0.5.0" +version = "0.6.0" dependencies = [ "serde", + "serde_bytes", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", ] [[package]] @@ -2767,6 +2970,16 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2779,9 +2992,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2825,7 +3038,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "block", "core-graphics-types", "foreign-types", @@ -2834,18 +3047,6 @@ dependencies = [ "paste", ] -[[package]] -name = "microserve" -version = "0.5.0" -dependencies = [ - "cpal", - "lonelyradio_types", - "once_cell", - "queues", - "rmp-serde", - "tokio", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2854,9 +3055,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -2876,24 +3077,27 @@ dependencies = [ [[package]] name = "monoclient" -version = "0.5.0" +version = "0.6.0" dependencies = [ "clap", "crossterm", + "lonelyradio_types", "monolib", ] [[package]] name = "monoclient-s" -version = "0.5.0" +version = "0.6.0" dependencies = [ + "lonelyradio_types", "monolib", "slint", + "zune-jpeg", ] [[package]] name = "monolib" -version = "0.5.0" +version = "0.6.0" dependencies = [ "byteorder", "claxon", @@ -2904,10 +3108,11 @@ dependencies = [ [[package]] name = "monoloader" -version = "0.4.0" +version = "0.6.0" dependencies = [ "clap", "hound", + "lonelyradio_types", "monolib", ] @@ -2917,7 +3122,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "jni-sys", "log", "ndk-sys 0.5.0+25.2.9519653", @@ -2933,7 +3138,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -2966,6 +3171,12 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.26.4" @@ -2984,7 +3195,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "libc", ] @@ -2999,6 +3210,22 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -3016,7 +3243,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -3028,6 +3255,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3044,7 +3282,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -3066,7 +3304,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -3148,9 +3386,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] @@ -3218,11 +3456,11 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b41438d2fc63c46c74a2203bf5ccd82c41ba04347b2fcf5754f230b167067d5" +checksum = "490d3a563d3122bf7c911a59b0add9389e5ec0f5f0c3ac6b91ff235a0e6a7f90" dependencies = [ - "ttf-parser 0.21.1", + "ttf-parser 0.24.0", ] [[package]] @@ -3233,9 +3471,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3249,9 +3487,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -3289,7 +3527,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -3312,9 +3550,9 @@ checksum = "b330c9d1b92dfe68442ca20b009c717d5f0b1e3cf4965e62f704c3c6e95a1305" [[package]] name = "piper" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -3358,13 +3596,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.4.0", "pin-project-lite", "rustix 0.38.34", "tracing", @@ -3393,14 +3631,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "primal-check" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" dependencies = [ "num-integer", ] @@ -3426,13 +3664,41 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.71", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "qttypes" version = "0.2.11" @@ -3445,16 +3711,16 @@ dependencies = [ ] [[package]] -name = "queues" -version = "1.1.0" +name = "quick-error" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" dependencies = [ "memchr", ] @@ -3498,6 +3764,56 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ba61c28ba24c0cf8406e025cb29a742637e3f70776e61c27a8a8b72a042d12" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -3510,6 +3826,26 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -3530,18 +3866,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -3551,9 +3887,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -3562,9 +3898,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "resvg" @@ -3582,9 +3918,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.37" +version = "0.8.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +checksum = "1aee83dc281d5a3200d37b299acd13b81066ea126a7f16f0eae70fc9aed241d9" dependencies = [ "bytemuck", ] @@ -3695,7 +4031,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.14", @@ -3714,7 +4050,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88117946aa1bfb53c2ae0643ceac6506337f44887f8c9fbfb43587b1cc52ba49" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bytemuck", "smallvec", "ttf-parser 0.20.0", @@ -3724,6 +4060,22 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -3768,9 +4120,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" dependencies = [ "ab_glyph", "log", @@ -3793,29 +4145,38 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.202" +name = "serde_bytes" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -3830,7 +4191,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -3895,6 +4256,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simplecss" version = "0.2.1" @@ -3933,7 +4303,7 @@ version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0a4a3df502b317456a34df181ac67a7a6443b0f2e4b883ac220dba1dcb3ce38" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "lazy_static", "skia-bindings", "windows", @@ -4000,7 +4370,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -4089,6 +4459,15 @@ dependencies = [ "x11rb 0.12.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spin_on" version = "0.1.1" @@ -4133,24 +4512,24 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -4372,9 +4751,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -4382,16 +4761,35 @@ dependencies = [ ] [[package]] -name = "tar" -version = "0.4.40" +name = "system-deps" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" + [[package]] name = "tempfile" version = "3.10.1" @@ -4421,22 +4819,33 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", ] [[package]] @@ -4467,21 +4876,22 @@ dependencies = [ [[package]] name = "tiny-xlib" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d" +checksum = "1d52f22673960ad13af14ff4025997312def1223bfa7c8e4949d099e6b3d5d1c" dependencies = [ "as-raw-xcb-connection", - "ctor", - "libloading 0.8.3", + "ctor-lite", + "libloading 0.8.4", + "pkg-config", "tracing", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -4494,9 +4904,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -4511,13 +4921,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -4547,14 +4957,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.15", ] [[package]] @@ -4590,15 +5000,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.13", ] [[package]] @@ -4620,7 +5030,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -4654,6 +5064,12 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" +[[package]] +name = "ttf-parser" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8686b91785aff82828ed725225925b33b4fde44c4bb15876e5f7c832724c420a" + [[package]] name = "typenum" version = "1.17.0" @@ -4753,9 +5169,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -4777,7 +5193,7 @@ dependencies = [ "log", "pico-args", "roxmltree", - "rustybuzz", + "rustybuzz 0.13.0", "simplecss", "siphasher", "strict-num", @@ -4791,9 +5207,26 @@ dependencies = [ [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -4821,7 +5254,7 @@ checksum = "68c1b85ec843d3bc60e9d65fa7e00ce6549416a25c267b5ea93e6c81e3aa66e5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -4867,7 +5300,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -4901,7 +5334,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4914,9 +5347,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wayland-backend" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +checksum = "269c04f203640d0da2092d1b8d89a2d081714ae3ac2f1b53e99f205740517198" dependencies = [ "cc", "downcast-rs", @@ -4928,11 +5361,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.2" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +checksum = "08bd0f46c069d3382a36c8666c1b9ccef32b8b04f41667ca1fef06a1adcc2982" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "rustix 0.38.34", "wayland-backend", "wayland-scanner", @@ -4944,16 +5377,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.1" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +checksum = "09414bcf0fd8d9577d73e9ac4659ebc45bcc9cff1980a350543ad8e50ee263b2" dependencies = [ "rustix 0.38.34", "wayland-client", @@ -4966,7 +5399,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4978,7 +5411,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4991,7 +5424,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5000,9 +5433,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.1" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +checksum = "edf466fc49a4feb65a511ca403fec3601494d0dee85dbf37fff6fa0dd4eec3b6" dependencies = [ "proc-macro2", "quick-xml", @@ -5011,9 +5444,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.1" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +checksum = "4a6754825230fa5b27bafaa28c30b3c9e72c55530581220cef401fa422c0fae7" dependencies = [ "dlib", "log", @@ -5051,6 +5484,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "which" version = "4.4.2" @@ -5112,7 +5551,7 @@ dependencies = [ "windows-core 0.54.0", "windows-implement", "windows-interface", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5121,7 +5560,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5131,7 +5570,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ "windows-result", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5142,7 +5581,7 @@ checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -5153,16 +5592,16 @@ checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5189,7 +5628,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5224,18 +5663,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -5252,9 +5691,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5270,9 +5709,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5288,15 +5727,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5312,9 +5751,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5330,9 +5769,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5348,9 +5787,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5366,9 +5805,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" @@ -5379,7 +5818,7 @@ dependencies = [ "ahash", "android-activity 0.5.2", "atomic-waker", - "bitflags 2.5.0", + "bitflags 2.6.0", "bytemuck", "calloop", "cfg_aliases 0.1.1", @@ -5430,9 +5869,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -5493,7 +5932,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname 0.4.3", "libc", - "libloading 0.8.3", + "libloading 0.8.4", "once_cell", "rustix 0.38.34", "x11rb-protocol 0.13.1", @@ -5533,12 +5972,12 @@ checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" [[package]] name = "xdg-home" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -5558,7 +5997,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "dlib", "log", "once_cell", @@ -5567,9 +6006,9 @@ dependencies = [ [[package]] name = "xkeysym" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" @@ -5651,22 +6090,46 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e45a193..4c9ff51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,19 +5,19 @@ members = [ "monoclient-s", "monolib", "monoloader", - "microserve", ] [package] name = "lonelyradio" description = "TCP radio for lonely ones" -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT" authors = ["Ivan Bushchik "] repository = "https://github.com/ivabus/lonelyradio" [dependencies] +lonelyradio_types = { version = "0.6.0", path = "./lonelyradio_types" } rand = "0.8.5" clap = { version = "4.4.18", features = ["derive"] } tokio = { version = "1.35.1", features = [ @@ -43,9 +43,12 @@ async-stream = "0.3.5" tokio-stream = { version = "0.1.15", features = ["sync"] } futures-util = "0.3.30" samplerate = "0.2.4" -lonelyradio_types = { version = "0.5.0", path = "./lonelyradio_types" } once_cell = "1.19.0" flacenc = { version = "0.4.0", default-features = false } +image = "0.25.1" + +[build-dependencies] +cc = "1.0.98" [profile.release] opt-level = 3 diff --git a/README.md b/README.md index 37f6f13..425ff71 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,90 @@ # lonelyradio -Broadcast audio over the internet. +Broadcast lossless audio over the internet. Decodes audio streams using [symphonia](https://github.com/pdeljanov/Symphonia). Optionally transcodes audio into and from FLAC using [flacenc-rs](https://github.com/yotarok/flacenc-rs/) and [claxon](https://github.com/ruuda/claxon). -## Installation - -### Install music server +## Install server ```shell -cargo install --git https://github.com/ivabus/lonelyradio --tag 0.5.0 lonelyradio -``` - -### Install CLI client - -```shell -cargo install --git https://github.com/ivabus/lonelyradio --tag 0.5.0 monoclient -``` - -### Install GUI (Slint) client - -```shell -cargo install --git https://github.com/ivabus/lonelyradio --tag 0.5.0 monoclient-s +cargo install --git https://github.com/ivabus/lonelyradio --tag 0.6.0 lonelyradio ``` ## Run ``` -lonelyradio [-a ] [-p|--public-log] [-w|--war] [-m|--max-samplerate M] [--xor-key-file FILE] [--no-resampling] [-f|--flac] +lonelyradio ``` All files (recursively) will be shuffled and played back. Public log will be displayed to stdout, private to stderr. -`-m|--max-samplerate M` will resample tracks which samplerate exceeds M to M - -`--xor-key-file FILE` will XOR all outgoing bytes looping through FILE - -`-f|--flac` will enable (experimental) FLAC compression +Look into `--help` for detailed info ### Clients -[monoclient](./monoclient) is a recommended CLI player for lonelyradio that uses [monolib](./monolib) +#### monoclient-x + +[monoclient-x](./monoclient-x) is a SwiftUI player for lonelyradio for iOS/iPadOS/macOS + +##### Build + +1. Build monolib with [xcframework](https://github.com/Binlogo/cargo-xcframework) +2. Build monoclient-x using Xcode or `xcodebuild` + +#### monoclient-s + +[monoclient-s](./monoclient-s) is a GUI player for lonelyradio built with [Slint](https://slint.dev) + + +##### Install + +```shell +cargo install --git https://github.com/ivabus/lonelyradio --tag 0.6.0 monoclient-s +``` + +You may need to install some dependencies for Slint. + +Desktop integration will be added later. + +##### Build + +``` +cargo build -p monoclient-s +``` + +You may need to install some dependencies for Slint. + +#### monoclient + +[monoclient](./monoclient) is a CLI player for lonelyradio that uses [monolib](./monolib) ```shell monoclient : ``` -[monoclient-s](./monoclient-s) is a experimental GUI player for lonelyradio built with [Slint](https://slint.dev) +##### Install monoclient ```shell -monoclient-s +cargo install --git https://github.com/ivabus/lonelyradio --tag 0.6.0 monoclient ``` -Desktop integration will be added later. - -### Other clients - -SwiftUI client is availible in [platform](./platform) directory. +# Other things [monoloader](./monoloader) is a tool, that allows you to download individual audio tracks from lonelyradio-compatible servers. [monolib](./monolib) provides a C API compatible with lonelyradio for creating custom clients. +The full protocol specification will be available later. If you would like to learn more about it now, please refer to the monolib. + #### monolib API stability As lonelyradio has not yet reached its first major release, the API may (and will) break at any point. ### Microphone server -Experimental server (lonelyradio-compatible) for streaming audio from your microphone is available in the [microserve](./microserve) crate. +Experimental (and uncompatible with versions 0.6+) server (lonelyradio-compatible) for streaming audio from your microphone is available in the [microserve](./microserve) crate. ## License diff --git a/lonelyradio_types/Cargo.toml b/lonelyradio_types/Cargo.toml index 650a965..327931c 100644 --- a/lonelyradio_types/Cargo.toml +++ b/lonelyradio_types/Cargo.toml @@ -2,10 +2,11 @@ name = "lonelyradio_types" description = "Shared types for lonelyradio" license = "MIT" -version = "0.5.0" +version = "0.6.0" edition = "2021" authors = ["Ivan Bushchik "] repository = "https://github.com/ivabus/lonelyradio" [dependencies] serde = { version = "1.0.197", features = ["derive"] } +serde_bytes = "0.11.15" diff --git a/lonelyradio_types/src/lib.rs b/lonelyradio_types/src/lib.rs index da10300..a6bbc09 100644 --- a/lonelyradio_types/src/lib.rs +++ b/lonelyradio_types/src/lib.rs @@ -1,25 +1,63 @@ use serde::{Deserialize, Serialize}; +pub const HELLO_MAGIC: u64 = 0x104e1374d10; + #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] pub enum Message { T(TrackMetadata), F(FragmentMetadata), } +#[repr(C)] +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +pub struct Settings { + #[serde(rename = "e")] + pub encoder: Encoder, + + #[serde(rename = "co")] + pub cover: i32, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +pub struct ServerCapabilities { + #[serde(rename = "e")] + pub encoders: Vec, +} + #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] pub struct TrackMetadata { + #[serde(rename = "tls")] pub track_length_secs: u64, + #[serde(rename = "tlf")] pub track_length_frac: f32, + #[serde(rename = "c")] pub channels: u16, + #[serde(rename = "sr")] pub sample_rate: u32, - pub flac: bool, + #[serde(rename = "e")] + pub encoder: Encoder, + #[serde(rename = "mt")] pub title: String, + #[serde(rename = "mal")] pub album: String, + #[serde(rename = "mar")] pub artist: String, + #[serde(rename = "co")] + #[serde(with = "serde_bytes")] + pub cover: Option>, +} + +#[repr(u8)] +#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq)] +pub enum Encoder { + Pcm16 = 0, + PcmFloat = 1, + Flac = 2, } #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] pub struct FragmentMetadata { - // In samples or bytes (if FLAC) + // In bytes + #[serde(rename = "l")] pub length: u64, } diff --git a/microserve/src/main.rs b/microserve/src/main.rs index 10f41ec..19c1c18 100644 --- a/microserve/src/main.rs +++ b/microserve/src/main.rs @@ -39,7 +39,8 @@ async fn update_start() { async fn stream(mut s: std::net::TcpStream) { println!("Playing for {}", s.peer_addr().unwrap()); let md = lonelyradio_types::Message::T(TrackMetadata { - flac: false, + cover: None, + encoder: lonelyradio_types::Encoder::Pcm, track_length_secs: 0, track_length_frac: 0.0, channels: 1, diff --git a/monoclient-s/Cargo.toml b/monoclient-s/Cargo.toml index 2aa9ba1..4accef0 100644 --- a/monoclient-s/Cargo.toml +++ b/monoclient-s/Cargo.toml @@ -1,19 +1,22 @@ [package] name = "monoclient-s" description = "Client for lonelyradio built with Slint" -version = "0.5.0" +version = "0.6.0" edition = "2021" [dependencies] -slint = { version = "1.6.0", features = ["backend-android-activity-06"] } -monolib = { path = "../monolib" } +slint = { version = "1.6", features = ["backend-android-activity-06"] } +monolib = { path = "../monolib", version = "0.6.0" } +lonelyradio_types = { version = "0.6.0", path = "../lonelyradio_types" } +zune-jpeg = "0.4.11" +[lib] +crate-type = [ "cdylib" ] -# TODO: Set up cargo-bundle -#[package.metadata.bundle] -#name = "monoclient-s" -#identifier = "dev.ivabus.monoclient-s" -#icon = ["lonelyradio.png", "lonelyradio.icns"] -#version = "0.5.0" -#copyright = "Copyright (c) 2024 Ivan Bushchik." -#category = "Music" +[package.metadata.bundle] +name = "monoclient-s" +identifier = "dev.ivabus.monoclient-s" +icon = ["lonelyradio.png", "lonelyradio.icns"] +version = "0.5.0" +copyright = "Copyright (c) 2024 Ivan Bushchik." +category = "Music" diff --git a/monoclient-s/lonelyradio.icns b/monoclient-s/lonelyradio.icns new file mode 100644 index 0000000..36c012c Binary files /dev/null and b/monoclient-s/lonelyradio.icns differ diff --git a/monoclient-s/lonelyradio.png b/monoclient-s/lonelyradio.png new file mode 100644 index 0000000..99bb9bb Binary files /dev/null and b/monoclient-s/lonelyradio.png differ diff --git a/monoclient-s/src/app.rs b/monoclient-s/src/app.rs new file mode 100644 index 0000000..e08b3f3 --- /dev/null +++ b/monoclient-s/src/app.rs @@ -0,0 +1,227 @@ +use std::time::Duration; + +use monolib::State; +use slint::{Image, Rgb8Pixel, Rgba8Pixel, SharedPixelBuffer, Weak}; + +slint::slint! { + import { AboutSlint, Button, VerticalBox, GroupBox, Slider } from "std-widgets.slint"; +export component MainWindow inherits Window { + max-height: self.preferred-height; + callback play; + callback stop; + callback next; + callback change_volume(float); + callback text_edited; + + in-out property addr: address.text; + in-out property mtitle: ""; + in-out property malbum: ""; + in-out property martist: ""; + in-out property volume: svolume.value; + in-out property start_enabled: false; + in-out property playing: false; + in-out property paused: false; + in property cover: @image-url("lonelyradio.png"); + + title: "monoclient-s"; + min-width: 192px; + max-width: 768px; + VerticalBox { + alignment: center; + spacing: 0px; + + Image { + source: cover; + max-height: 192px; + max-width: 192px; + min-height: 192px; + min-width: 192px; + } + + GroupBox{ + max-width: 768px; + address := TextInput { + text: ""; + horizontal-alignment: center; + height: 1.25rem; + + accepted => { + self.clear_focus() + } + + edited => { + text_edited() + } + } + } + + VerticalLayout { + max-width: 512px; + + VerticalLayout { + spacing: 4px; + Button { + max-width: 256px; + text: playing ? (paused ? "Play" : "Pause") : "Start"; + enabled: start_enabled || playing; + clicked => { + play() + } + } + HorizontalLayout { + spacing: 4px; + max-width: 256px; + Button { + text: "Stop"; + enabled: playing && !paused; + clicked => { + stop() + } + } + Button { + text: "Next"; + enabled: playing && !paused; + clicked => { + next() + } + } + } + svolume := Slider { + value: 255; + maximum: 255; + changed(f) => { + change_volume(f) + } + } + } + } + + VerticalLayout { + padding: 4px; + tartist := Text { + height: 1.25rem; + font-weight: 600; + text: martist; + overflow: elide; + } + talbum := Text { + height: 1.25rem; + text: malbum; + overflow: elide; + } + ttitle := Text { + height: 1.25rem; + text: mtitle; + overflow: elide; + } + } + } +} +} + +fn start_playback(window_weak: Weak) { + let window = window_weak.upgrade().unwrap(); + let addr = window.get_addr().to_string(); + let handle = std::thread::spawn(move || { + monolib::run( + &addr, + lonelyradio_types::Settings { + encoder: lonelyradio_types::Encoder::Flac, + cover: 512, + }, + ) + }); + std::thread::sleep(Duration::from_millis(166)); + if handle.is_finished() { + window.set_playing(false); + return; + } + window.set_playing(true); + window.set_paused(false); + while monolib::get_metadata().is_none() {} + monolib::set_volume(window.get_volume() as u8); +} + +pub fn _main() { + let window = MainWindow::new().unwrap(); + + let window_weak = window.as_weak(); + window.on_text_edited(move || { + let window = window_weak.upgrade().unwrap(); + let addr = window.get_addr().to_string(); + window.set_start_enabled(addr.contains(':')); + }); + + let window_weak = window.as_weak(); + window.on_play(move || match monolib::get_state() { + State::NotStarted => start_playback(window_weak.clone()), + State::Paused => { + let window = window_weak.upgrade().unwrap(); + window.set_paused(false); + monolib::toggle(); + } + State::Resetting => {} + State::Playing => { + let window = window_weak.upgrade().unwrap(); + window.set_paused(true); + monolib::toggle() + } + }); + + let window_weak = window.as_weak(); + window.on_next(move || { + monolib::stop(); + start_playback(window_weak.clone()) + }); + let window_weak = window.as_weak(); + window.on_stop(move || { + let window = window_weak.upgrade().unwrap(); + window.set_playing(false); + window.set_martist("".into()); + window.set_malbum("".into()); + window.set_mtitle("".into()); + window.set_cover(Image::from_rgba8(SharedPixelBuffer::::new(1, 1))); + monolib::stop(); + }); + window.on_change_volume(move |vol| monolib::set_volume(vol as u8)); + let window_weak = window.as_weak(); + std::thread::spawn(move || loop { + let window = window_weak.clone(); + while monolib::get_metadata().is_none() { + std::thread::sleep(Duration::from_millis(25)) + } + let md = monolib::get_metadata().unwrap(); + let _md = md.clone(); + if let Some(jpeg) = md.cover { + let mut decoder = zune_jpeg::JpegDecoder::new(jpeg); + decoder.decode_headers().unwrap(); + let (w, h) = decoder.dimensions().unwrap(); + let decoded = decoder.decode().unwrap(); + let mut pixel_buffer = SharedPixelBuffer::::new(w as u32, h as u32); + pixel_buffer.make_mut_bytes().copy_from_slice(&decoded); + window + .upgrade_in_event_loop(|win| { + let image = Image::from_rgb8(pixel_buffer); + win.set_cover(image); + }) + .unwrap(); + } else { + window + .upgrade_in_event_loop(|win| { + win.set_cover(Image::from_rgba8(SharedPixelBuffer::::new(1, 1))); + }) + .unwrap(); + } + slint::invoke_from_event_loop(move || { + let window = window.unwrap(); + window.set_martist(md.artist.clone().into()); + window.set_malbum(md.album.clone().into()); + window.set_mtitle(md.title.clone().into()); + }) + .unwrap(); + while monolib::get_metadata() == Some(_md.clone()) { + std::thread::sleep(Duration::from_millis(100)) + } + }); + window.run().unwrap(); +} diff --git a/monoclient-s/src/lib.rs b/monoclient-s/src/lib.rs new file mode 100644 index 0000000..87bfb0e --- /dev/null +++ b/monoclient-s/src/lib.rs @@ -0,0 +1,7 @@ +mod app; +#[cfg(target_os = "andoid")] +#[no_mangle] +fn android_main(app: slint::android::AndroidApp) { + slint::android::init(app).unwrap(); + app::_main(); +} diff --git a/monoclient-s/src/main.rs b/monoclient-s/src/main.rs index 8cbb1f5..fb31ded 100644 --- a/monoclient-s/src/main.rs +++ b/monoclient-s/src/main.rs @@ -1,181 +1,4 @@ -use std::time::Duration; - -use monolib::State; -use slint::Weak; - -slint::slint! { - import { AboutSlint, Button, VerticalBox, GroupBox, Slider } from "std-widgets.slint"; -export component MainWindow inherits Window { - max-height: self.preferred-height; - callback play; - callback stop; - callback next; - callback change_volume(float); - callback text_edited; - - in-out property addr: address.text; - in-out property mtitle: ""; - in-out property malbum: ""; - in-out property martist: ""; - in-out property volume: svolume.value; - in-out property start_enabled: false; - in-out property playing: false; - in-out property paused: false; - - title: "monoclient-s"; - min-width: 192px; - max-width: 768px; - VerticalBox { - alignment: center; - GroupBox{ - max-width: 768px; - address := TextInput { - text: ""; - horizontal-alignment: center; - height: 1.25rem; - - accepted => { - self.clear_focus() - } - - edited => { - text_edited() - } - } - } - VerticalLayout { - max-width: 512px; - VerticalLayout { - spacing: 4px; - Button { - max-width: 256px; - text: playing ? (paused ? "Play" : "Pause") : "Start"; - enabled: start_enabled || playing; - clicked => { - play() - } - } - HorizontalLayout { - spacing: 4px; - max-width: 256px; - Button { - text: "Stop"; - enabled: playing && !paused; - clicked => { - stop() - } - } - Button { - text: "Next"; - enabled: playing && !paused; - clicked => { - next() - } - } - } - svolume := Slider { - value: 255; - maximum: 255; - changed(f) => { - change_volume(f) - } - } - } - tartist := Text { - height: 1.25rem; - font-weight: 600; - text: martist; - overflow: elide; - } - talbum := Text { - height: 1.25rem; - text: malbum; - overflow: elide; - } - ttitle := Text { - height: 1.25rem; - text: mtitle; - overflow: elide; - } - } - } -} -} - -fn start_playback(window_weak: Weak) { - let window = window_weak.upgrade().unwrap(); - let addr = window.get_addr().to_string(); - let handle = std::thread::spawn(move || monolib::run(&addr, None)); - std::thread::sleep(Duration::from_millis(166)); - if handle.is_finished() { - window.set_playing(false); - return; - } - window.set_playing(true); - window.set_paused(false); - while monolib::get_metadata().is_none() {} - monolib::set_volume(window.get_volume() as u8); -} - -pub fn main() { - let window = MainWindow::new().unwrap(); - - let window_weak = window.as_weak(); - window.on_text_edited(move || { - let window = window_weak.upgrade().unwrap(); - let addr = window.get_addr().to_string(); - window.set_start_enabled(addr.contains(':')); - }); - - let window_weak = window.as_weak(); - window.on_play(move || match monolib::get_state() { - State::NotStarted => start_playback(window_weak.clone()), - State::Paused => { - let window = window_weak.upgrade().unwrap(); - window.set_paused(false); - monolib::toggle(); - } - State::Resetting => {} - State::Playing => { - let window = window_weak.upgrade().unwrap(); - window.set_paused(true); - monolib::toggle() - } - }); - - let window_weak = window.as_weak(); - window.on_next(move || { - monolib::stop(); - start_playback(window_weak.clone()) - }); - let window_weak = window.as_weak(); - window.on_stop(move || { - let window = window_weak.upgrade().unwrap(); - window.set_playing(false); - window.set_martist("".into()); - window.set_malbum("".into()); - window.set_mtitle("".into()); - monolib::stop(); - }); - window.on_change_volume(move |vol| monolib::set_volume(vol as u8)); - let window_weak = window.as_weak(); - std::thread::spawn(move || loop { - let window = window_weak.clone(); - while monolib::get_metadata().is_none() { - std::thread::sleep(Duration::from_millis(25)) - } - let md = monolib::get_metadata().unwrap(); - let _md = md.clone(); - slint::invoke_from_event_loop(move || { - let window = window.unwrap(); - window.set_martist(md.artist.clone().into()); - window.set_malbum(md.album.clone().into()); - window.set_mtitle(md.title.clone().into()); - }) - .unwrap(); - while monolib::get_metadata() == Some(_md.clone()) { - std::thread::sleep(Duration::from_millis(100)) - } - }); - window.run().unwrap(); +mod app; +fn main() { + app::_main() } diff --git a/monoclient-x/monoclient-x.xcodeproj/project.pbxproj b/monoclient-x/monoclient-x.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f528c8f --- /dev/null +++ b/monoclient-x/monoclient-x.xcodeproj/project.pbxproj @@ -0,0 +1,453 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 4F79BFA42C19977F00074B09 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F79BFA32C19975000074B09 /* libresolv.tbd */; }; + 4F92D0562C4176A200CF3363 /* MonoLib.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F92D0552C4176A200CF3363 /* MonoLib.xcframework */; }; + 4F92D0572C4176A200CF3363 /* MonoLib.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F92D0552C4176A200CF3363 /* MonoLib.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4FAD30F72C1980D900074B09 /* monoclient_xApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAD30F62C1980D900074B09 /* monoclient_xApp.swift */; }; + 4FAD30F92C1980D900074B09 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAD30F82C1980D900074B09 /* ContentView.swift */; }; + 4FAD30FB2C1980D900074B09 /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAD30FA2C1980D900074B09 /* Metadata.swift */; }; + 4FAD30FD2C1980DC00074B09 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FAD30FC2C1980DC00074B09 /* Assets.xcassets */; }; + 4FAD31012C1980DC00074B09 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FAD31002C1980DC00074B09 /* Preview Assets.xcassets */; }; + 4FAE6E662C1B5EB100074B09 /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAE6E652C1B5EB100074B09 /* Player.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4F15BE3A2C3AF1840026AC81 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 4FF5DF8A2C41575B0039B22C /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 4F92D0572C4176A200CF3363 /* MonoLib.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4F15BE242C3AF1810026AC81 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 4F79BF922C19903C00074B09 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 4F79BFA32C19975000074B09 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; }; + 4F92D0552C4176A200CF3363 /* MonoLib.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MonoLib.xcframework; path = ../target/MonoLib.xcframework; sourceTree = ""; }; + 4FAD30F32C1980D900074B09 /* monoclient-x.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "monoclient-x.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4FAD30F62C1980D900074B09 /* monoclient_xApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = monoclient_xApp.swift; sourceTree = ""; }; + 4FAD30F82C1980D900074B09 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 4FAD30FA2C1980D900074B09 /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = ""; }; + 4FAD30FC2C1980DC00074B09 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4FAD30FE2C1980DC00074B09 /* monoclient_x.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = monoclient_x.entitlements; sourceTree = ""; }; + 4FAD31002C1980DC00074B09 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 4FAE6E652C1B5EB100074B09 /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4FAD30F02C1980D900074B09 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4F92D0562C4176A200CF3363 /* MonoLib.xcframework in Frameworks */, + 4F79BFA42C19977F00074B09 /* libresolv.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4F79BF942C1992ED00074B09 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4F92D0552C4176A200CF3363 /* MonoLib.xcframework */, + 4F79BFA32C19975000074B09 /* libresolv.tbd */, + 4F15BE242C3AF1810026AC81 /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4FAD30EA2C1980D900074B09 = { + isa = PBXGroup; + children = ( + 4FAD30F52C1980D900074B09 /* monoclient-x */, + 4FAD30F42C1980D900074B09 /* Products */, + 4F79BF942C1992ED00074B09 /* Frameworks */, + ); + sourceTree = ""; + }; + 4FAD30F42C1980D900074B09 /* Products */ = { + isa = PBXGroup; + children = ( + 4FAD30F32C1980D900074B09 /* monoclient-x.app */, + ); + name = Products; + sourceTree = ""; + }; + 4FAD30F52C1980D900074B09 /* monoclient-x */ = { + isa = PBXGroup; + children = ( + 4F79BF922C19903C00074B09 /* Info.plist */, + 4FAD30F62C1980D900074B09 /* monoclient_xApp.swift */, + 4FAD30F82C1980D900074B09 /* ContentView.swift */, + 4FAD30FA2C1980D900074B09 /* Metadata.swift */, + 4FAD30FC2C1980DC00074B09 /* Assets.xcassets */, + 4FAD30FE2C1980DC00074B09 /* monoclient_x.entitlements */, + 4FAD30FF2C1980DC00074B09 /* Preview Content */, + 4FAE6E652C1B5EB100074B09 /* Player.swift */, + ); + path = "monoclient-x"; + sourceTree = ""; + }; + 4FAD30FF2C1980DC00074B09 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 4FAD31002C1980DC00074B09 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4FAD30F22C1980D900074B09 /* monoclient-x */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4FAD31042C1980DC00074B09 /* Build configuration list for PBXNativeTarget "monoclient-x" */; + buildPhases = ( + 4FAD30EF2C1980D900074B09 /* Sources */, + 4FAD30F02C1980D900074B09 /* Frameworks */, + 4FAD30F12C1980D900074B09 /* Resources */, + 4F15BE3A2C3AF1840026AC81 /* Embed Foundation Extensions */, + 4FF5DF8A2C41575B0039B22C /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "monoclient-x"; + packageProductDependencies = ( + ); + productName = "monoclient-x"; + productReference = 4FAD30F32C1980D900074B09 /* monoclient-x.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4FAD30EB2C1980D900074B09 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + 4FAD30F22C1980D900074B09 = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = 4FAD30EE2C1980D900074B09 /* Build configuration list for PBXProject "monoclient-x" */; + compatibilityVersion = "Xcode 15.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4FAD30EA2C1980D900074B09; + packageReferences = ( + ); + productRefGroup = 4FAD30F42C1980D900074B09 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4FAD30F22C1980D900074B09 /* monoclient-x */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4FAD30F12C1980D900074B09 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4FAD31012C1980DC00074B09 /* Preview Assets.xcassets in Resources */, + 4FAD30FD2C1980DC00074B09 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4FAD30EF2C1980D900074B09 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4FAE6E662C1B5EB100074B09 /* Player.swift in Sources */, + 4FAD30F92C1980D900074B09 /* ContentView.swift in Sources */, + 4FAD30FB2C1980D900074B09 /* Metadata.swift in Sources */, + 4FAD30F72C1980D900074B09 /* monoclient_xApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4FAD31022C1980DC00074B09 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4FAD31032C1980DC00074B09 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 4FAD31052C1980DC00074B09 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_USE_OPTIMIZATION_PROFILE = YES; + CODE_SIGN_ENTITLEMENTS = "monoclient-x/monoclient_x.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"monoclient-x/Preview Content\""; + DEVELOPMENT_TEAM = F5PQ7AR4DP; + ENABLE_HARDENED_RUNTIME = YES; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "${SRCROOT}/../target/**"; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ""; + INFOPLIST_FILE = "monoclient-x/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "monoclient-x"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = ""; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 0.6.0; + PRODUCT_BUNDLE_IDENTIFIER = "dev.ivabus.monoclient-x"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + XROS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + 4FAD31062C1980DC00074B09 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_USE_OPTIMIZATION_PROFILE = YES; + CODE_SIGN_ENTITLEMENTS = "monoclient-x/monoclient_x.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"monoclient-x/Preview Content\""; + DEVELOPMENT_TEAM = F5PQ7AR4DP; + ENABLE_HARDENED_RUNTIME = YES; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "${SRCROOT}/../target/**"; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ""; + INFOPLIST_FILE = "monoclient-x/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "monoclient-x"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = "${PROJECT_DIR/../target/aarch64-apple-darwin/release}"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 0.6.0; + PRODUCT_BUNDLE_IDENTIFIER = "dev.ivabus.monoclient-x"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + XROS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4FAD30EE2C1980D900074B09 /* Build configuration list for PBXProject "monoclient-x" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FAD31022C1980DC00074B09 /* Debug */, + 4FAD31032C1980DC00074B09 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4FAD31042C1980DC00074B09 /* Build configuration list for PBXNativeTarget "monoclient-x" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FAD31052C1980DC00074B09 /* Debug */, + 4FAD31062C1980DC00074B09 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4FAD30EB2C1980D900074B09 /* Project object */; +} diff --git a/monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/platform/swiftui/monoclient/monoclient.entitlements b/monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 58% rename from platform/swiftui/monoclient/monoclient.entitlements rename to monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index f2ef3ae..18d9810 100644 --- a/platform/swiftui/monoclient/monoclient.entitlements +++ b/monoclient-x/monoclient-x.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -2,9 +2,7 @@ - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - + IDEDidComputeMac32BitWarning + diff --git a/monoclient-x/monoclient-x.xcodeproj/xcshareddata/xcschemes/monoclient-x.xcscheme b/monoclient-x/monoclient-x.xcodeproj/xcshareddata/xcschemes/monoclient-x.xcscheme new file mode 100644 index 0000000..481d2a3 --- /dev/null +++ b/monoclient-x/monoclient-x.xcodeproj/xcshareddata/xcschemes/monoclient-x.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/swiftui/monoclient/Assets.xcassets/AccentColor.colorset/Contents.json b/monoclient-x/monoclient-x/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 66% rename from platform/swiftui/monoclient/Assets.xcassets/AccentColor.colorset/Contents.json rename to monoclient-x/monoclient-x/Assets.xcassets/AccentColor.colorset/Contents.json index 2c9cdaf..a9f9aaa 100644 --- a/platform/swiftui/monoclient/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/monoclient-x/monoclient-x/Assets.xcassets/AccentColor.colorset/Contents.json @@ -2,8 +2,8 @@ "colors" : [ { "color" : { - "platform" : "ios", - "reference" : "systemPinkColor" + "platform" : "universal", + "reference" : "systemPurpleColor" }, "idiom" : "universal" } diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/Contents.json b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 64% rename from platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/Contents.json rename to monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Contents.json index a1e9135..ec1e6e4 100644 --- a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,31 @@ { "images" : [ { - "filename" : "icon-1024@1x.png", + "filename" : "monoclient-x-ios.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "monoclient-x-ios 1.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "monoclient-x-ios 2.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -49,19 +73,19 @@ "size" : "256x256" }, { - "filename" : "icon-256@2x.png", + "filename" : "Icon-macOS-512x512@1x 1.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { - "filename" : "icon-512@1x.png", + "filename" : "Icon-macOS-512x512@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "icon-512@2x.png", + "filename" : "Icon-macOS-512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x 1.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x 1.png new file mode 100644 index 0000000..d8cc89f Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x 1.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x.png new file mode 100644 index 0000000..d8cc89f Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@1x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@2x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@2x.png new file mode 100644 index 0000000..63ad7b5 Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/Icon-macOS-512x512@2x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png new file mode 100644 index 0000000..c2e7f12 Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png new file mode 100644 index 0000000..6a1f3fa Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png new file mode 100644 index 0000000..c81bdb5 Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png new file mode 100644 index 0000000..5b6bd0d Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png new file mode 100644 index 0000000..6a1f3fa Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png new file mode 100644 index 0000000..5b6bd0d Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png new file mode 100644 index 0000000..38d9f22 Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 1.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 1.png new file mode 100644 index 0000000..ed3447a Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 1.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 2.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 2.png new file mode 100644 index 0000000..ed3447a Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios 2.png differ diff --git a/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios.png b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios.png new file mode 100644 index 0000000..ed3447a Binary files /dev/null and b/monoclient-x/monoclient-x/Assets.xcassets/AppIcon.appiconset/monoclient-x-ios.png differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/Contents.json b/monoclient-x/monoclient-x/Assets.xcassets/Contents.json similarity index 100% rename from platform/swiftui/monoclient/Assets.xcassets/Contents.json rename to monoclient-x/monoclient-x/Assets.xcassets/Contents.json diff --git a/monoclient-x/monoclient-x/ContentView.swift b/monoclient-x/monoclient-x/ContentView.swift new file mode 100644 index 0000000..dfeb255 --- /dev/null +++ b/monoclient-x/monoclient-x/ContentView.swift @@ -0,0 +1,71 @@ +// +// ContentView.swift +// monoclient-x +// +// Created by ivabus on 12.06.2024. +// + +import SwiftUI +import SwiftData +import AppIntents + +struct ContentView: View { + var body: some View { + Player() + } +} + +struct PlayIntent: AudioPlaybackIntent { + static var title: LocalizedStringResource = "Start lonelyradio client" + static var description = IntentDescription("Plays from setted up server") + + static var openAppWhenRun: Bool = false + static var isDiscoverable: Bool = true + + @MainActor + func perform() async throws -> some IntentResult { + Player().play() + return .result() + } +} + +struct StopIntent: AudioPlaybackIntent { + static var title: LocalizedStringResource = "Stop lonelyradio client" + static var description = IntentDescription("Stops monoclient") + + static var openAppWhenRun: Bool = false + static var isDiscoverable: Bool = true + + @MainActor + func perform() async throws -> some IntentResult { + Player().stop() + return .result() + } +} + +struct LibraryAppShortcuts: AppShortcutsProvider { + static var appShortcuts: [AppShortcut] { + AppShortcut( + intent: PlayIntent(), + phrases: [ + "Start playback \(.applicationName)", + ], + shortTitle: "Start monoclient", + systemImageName: "infinity.circle" + ) + AppShortcut( + intent: StopIntent(), + phrases: [ + "Stop playback in \(.applicationName)" + ], + shortTitle: "Stop monoclient", + systemImageName: "stop.fill" + ) + } + + +} + +#Preview { + ContentView() +} diff --git a/platform/swiftui/monoclient/Info.plist b/monoclient-x/monoclient-x/Info.plist similarity index 100% rename from platform/swiftui/monoclient/Info.plist rename to monoclient-x/monoclient-x/Info.plist diff --git a/monoclient-x/monoclient-x/Metadata.swift b/monoclient-x/monoclient-x/Metadata.swift new file mode 100644 index 0000000..fad7c02 --- /dev/null +++ b/monoclient-x/monoclient-x/Metadata.swift @@ -0,0 +1,57 @@ +// +// Item.swift +// monoclient-x +// +// Created by ivabus on 12.06.2024. +// + +import CoreGraphics +import CoreFoundation +import SwiftUI +import MonoLib + +#if os(macOS) +typealias PlatformImage = NSImage +#else +typealias PlatformImage = UIImage +#endif + +struct Metadata { + public var title: String + public var album: String + public var artist: String + + mutating func update() { + self.title = String(cString: c_get_metadata_title()) + self.album = String(cString: c_get_metadata_album()) + self.artist = String(cString: c_get_metadata_artist()) + } +} + +extension Metadata: Equatable { + static func == (lhs: Self, rhs: Self) -> Bool { + (lhs.album == rhs.album) && (lhs.artist == rhs.artist) && (lhs.title == rhs.title) + } +} + +struct Cover { + public var cover: PlatformImage + + mutating func update() { + let cov = c_get_cover_jpeg() + if cov.length != 0 { + let data = CFDataCreate(kCFAllocatorDefault, cov.bytes, Int(cov.length))! +#if os(macOS) + self.cover = PlatformImage(cgImage: CGImage(jpegDataProviderSource: CGDataProvider(data: data)!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.absoluteColorimetric)!, size: NSSize.init(width: 768, height:768)) +#else + self.cover = PlatformImage(cgImage: CGImage(jpegDataProviderSource: CGDataProvider(data: data)!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.absoluteColorimetric)!).preparingForDisplay()! +#endif + // deallocating memory + c_drop(cov.bytes, Int(cov.length)) + print(self.cover.size) + + } else { + self.cover = PlatformImage() + } + } +} diff --git a/monoclient-x/monoclient-x/Player.swift b/monoclient-x/monoclient-x/Player.swift new file mode 100644 index 0000000..3183b76 --- /dev/null +++ b/monoclient-x/monoclient-x/Player.swift @@ -0,0 +1,271 @@ +// +// Player.swift +// monoclient-x +// +// Created by ivabus on 13.06.2024. +// + +import SwiftUI +import AVFAudio +import MediaPlayer +import MonoLib + + +enum PlayerState { + case NotStarted + case Playing + case Paused + + mutating func update() { + self = switch c_get_state() { + case 2: PlayerState.Playing + case 3: PlayerState.Paused + default: PlayerState.NotStarted + } + } +} + +enum EncoderType: UInt8 { + case PCM16 = 0 + case PCMFloat = 1 + case FLAC = 2 +} + +enum CoverSize: Int32 { + case Full = 0 + case High = 768 + case Medium = 512 + case Low = 256 + case Min = 128 + case NoCover = -1 +} + + +struct Settings { + var encoder: EncoderType = EncoderType.FLAC + var cover_size: CoverSize = CoverSize.High/* + init(enc: EncoderType, cov: CoverSize) { + encoder = enc + cover_size = cov + }*/ +} + +struct Player: View { + + let timer_state = Timer.publish(every: 0.25, on: .main, in: .common).autoconnect() + let timer_meta = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect() + @State var metadata: Metadata = Metadata(title: "", album: "", artist: "") + @State var prev_meta: Metadata = Metadata(title: "", album: "", artist: "") + @State var cover: Cover = Cover(cover: PlatformImage()) + @State var state: PlayerState = PlayerState.NotStarted + @State var settings: Settings = Settings.init() + @AppStorage("ContentView.server") var server: String = "" + + var body: some View { + + VStack(alignment: .center) { +#if os(macOS) + Image(nsImage: cover.cover) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(minWidth: 256, maxWidth: 256, minHeight: 256, maxHeight: 256) + .frame(width: 256.0, height: 256.0) + .clipShape(.rect(cornerRadius: 24)) + .shadow(radius: 16) + .padding(16) +#else + Image(uiImage: cover.cover) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(minWidth: 256, maxWidth: 256, minHeight: 256, maxHeight: 256) + .frame(width: 256.0, height: 256.0) + .clipShape(.rect(cornerRadius: 24)) + .shadow(radius: 16) + .padding(16) +#endif + + VStack(alignment: .center){ + Text(metadata.title).bold() + + Text(metadata.album) + + Text(metadata.artist) + }.frame(minHeight: 64) + + TextField( + "Server", + text: $server, + onCommit: { +#if os(macOS) + DispatchQueue.main.async { + NSApp.keyWindow?.makeFirstResponder(nil) + } +#endif + } + ) + .disableAutocorrection(true) + .frame(width: 256) + .textFieldStyle(.roundedBorder) + .padding(16) + .multilineTextAlignment(.center) + + HStack(spacing: 8) { + Button(action: stop){ + Image(systemName: "stop.fill").padding(4).frame(width: 32, height: 24) + } + .disabled(state == PlayerState.NotStarted) + .buttonStyle(.bordered) + .font(.system(size: 20)) + .buttonBorderShape(.capsule) + + Button(action: play){ + Image(systemName: state == PlayerState.NotStarted ? "infinity.circle" : (state == PlayerState.Playing) ? "pause.circle.fill" : "play.circle" ) + .font(.system(size: 30)) + .padding(4) + } + .buttonStyle(.borderedProminent) + .buttonBorderShape(.capsule) + + Button(action: next){ + Image(systemName: "forward.end.fill").padding(4).frame(width: 32, height: 24) + }.disabled(state == PlayerState.NotStarted) + .buttonStyle(.bordered) + .font(.system(size: 20)) + .buttonBorderShape(.capsule) + } + Menu { + Picker("Encoder", selection: $settings.encoder) { + Text("PCM (s16)") + .tag(EncoderType.PCM16) + Text("PCM (f32)") + .tag(EncoderType.PCMFloat) + Text("FLAC (s24)") + .tag(EncoderType.FLAC) + }.pickerStyle(.menu) + + Picker("Cover size", selection: $settings.cover_size) { + Text("Original") + .tag(CoverSize.Full) + Text("High (768)") + .tag(CoverSize.High) + Text("Medium (512)") + .tag(CoverSize.Medium) + Text("Low (256)") + .tag(CoverSize.Low) + Text("Min (128)") + .tag(CoverSize.Min) + Text("No cover") + .tag(CoverSize.NoCover) + }.pickerStyle(.menu) + } label: { + Label("Settings", systemImage: "gearshape") + .padding(16) + }.frame(maxWidth: 128) + } + .padding(32) + .onReceive(timer_state) { _ in + state.update() + + #if os(macOS) + MPNowPlayingInfoCenter.default().playbackState = state == PlayerState.Playing ? .playing : .paused + #endif + + } + .onReceive(timer_meta) { _ in + metadata.update() + if prev_meta != metadata || metadata.album == "" || cover.cover == PlatformImage() { + prev_meta = metadata + cover.update() + } + let image = cover.cover + let mediaArtwork = MPMediaItemArtwork(boundsSize: image.size) { (size: CGSize) -> PlatformImage in + return image + } + + let nowPlayingInfo: [String: Any] = [ + MPMediaItemPropertyArtist: metadata.artist, + MPMediaItemPropertyAlbumTitle: metadata.album, + MPMediaItemPropertyTitle: metadata.title, + MPMediaItemPropertyArtwork: mediaArtwork, + MPNowPlayingInfoPropertyIsLiveStream: true, + MPMediaItemPropertyPlaybackDuration: c_get_metadata_length(), + + ] + MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo + + } + .onAppear() { +#if os(iOS) + UIApplication.shared.beginReceivingRemoteControlEvents() +#endif + MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false + MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = true + MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = false + MPRemoteCommandCenter.shared().skipBackwardCommand.isEnabled = false + MPRemoteCommandCenter.shared().pauseCommand.addTarget(handler: { _ in + if state != PlayerState.Paused { + play() + } + return MPRemoteCommandHandlerStatus.success + }) + MPRemoteCommandCenter.shared().playCommand.addTarget(handler: { _ in + if state != PlayerState.Playing { + play() + } + return MPRemoteCommandHandlerStatus.success + }) + + MPRemoteCommandCenter.shared().togglePlayPauseCommand.addTarget(handler: {_ in + play() + return MPRemoteCommandHandlerStatus.success + }) + + MPRemoteCommandCenter.shared().nextTrackCommand.addTarget(handler: {_ in + next() + return MPRemoteCommandHandlerStatus.success + }) + + + } + .animation(.spring, value: UUID()) + } + + + + + func play() { + switch state { + case PlayerState.NotStarted: do { +#if os(iOS) + let audioSession = AVAudioSession.sharedInstance() + do { + try audioSession.setCategory( + .playback, mode: .default) + try audioSession.setActive(true) + + } catch { + print("Failed to set the audio session configuration") + } +#endif + Thread.detachNewThread { + c_start(server, CSettings(encoder: settings.encoder.rawValue, cover: settings.cover_size.rawValue)) + } + } + default: do { + c_toggle() + state.update() + } + } + + } + func stop() { + c_stop() + state.update() + cover = Cover(cover: PlatformImage()) + } + func next() { + c_stop() + state.update() + play() + } +} diff --git a/platform/swiftui/monoclient/Preview Content/Preview Assets.xcassets/Contents.json b/monoclient-x/monoclient-x/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from platform/swiftui/monoclient/Preview Content/Preview Assets.xcassets/Contents.json rename to monoclient-x/monoclient-x/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/monoclient-x/monoclient-x/monoclient_x.entitlements b/monoclient-x/monoclient-x/monoclient_x.entitlements new file mode 100644 index 0000000..ee95ab7 --- /dev/null +++ b/monoclient-x/monoclient-x/monoclient_x.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/monoclient-x/monoclient-x/monoclient_xApp.swift b/monoclient-x/monoclient-x/monoclient_xApp.swift new file mode 100644 index 0000000..61da8ac --- /dev/null +++ b/monoclient-x/monoclient-x/monoclient_xApp.swift @@ -0,0 +1,35 @@ +// +// monoclient_xApp.swift +// monoclient-x +// +// Created by ivabus on 12.06.2024. +// + +import SwiftUI + +@main +struct monoclient_xApp: App { + + var body: some Scene { +#if os(macOS) + WindowGroup { + ContentView().onAppear { + NSWindow.allowsAutomaticWindowTabbing = false + } + .containerBackground(.ultraThinMaterial, for: .window) + .windowFullScreenBehavior(.disabled) + .windowResizeBehavior(.disabled) + }.defaultSize(width: 256, height: 512) + .windowStyle(.hiddenTitleBar) + .commands { + CommandGroup(replacing: CommandGroupPlacement.newItem) { + } + } +#else + WindowGroup { + ContentView() + } + .defaultSize(width: 256, height: 512) +#endif + } +} diff --git a/monoclient/Cargo.toml b/monoclient/Cargo.toml index 8ed711f..a382a77 100644 --- a/monoclient/Cargo.toml +++ b/monoclient/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "monoclient" license = "MIT" -version = "0.5.0" +version = "0.6.0" edition = "2021" authors = ["Ivan Bushchik "] repository = "https://github.com/ivabus/lonelyradio" [dependencies] -monolib = { version = "0.5.0", path = "../monolib" } +monolib = { version = "0.6.0", path = "../monolib" } clap = { version = "4.4.18", features = ["derive"] } crossterm = "0.27.0" +lonelyradio_types = { version = "0.6.0", path = "../lonelyradio_types" } + diff --git a/monoclient/src/main.rs b/monoclient/src/main.rs index 1d3d980..d09930b 100644 --- a/monoclient/src/main.rs +++ b/monoclient/src/main.rs @@ -3,10 +3,14 @@ use crossterm::cursor::MoveToColumn; use crossterm::event::{poll, read, Event}; use crossterm::style::Print; use crossterm::terminal::{Clear, ClearType}; +use lonelyradio_types::{Encoder, Settings}; use std::io::stdout; use std::path::PathBuf; +use std::sync::OnceLock; use std::time::Instant; +static VERBOSE: OnceLock = OnceLock::new(); + #[derive(Parser)] struct Args { /// Remote address @@ -14,6 +18,9 @@ struct Args { #[arg(long)] xor_key_file: Option, + + #[arg(short, long)] + verbose: bool, } const HELP: &str = r#"Keybinds: @@ -22,16 +29,31 @@ const HELP: &str = r#"Keybinds: Q - Quit monoclient H - Show this help"#; +macro_rules! verbose { + ($($arg:tt)*) => {{ + if *VERBOSE.get().unwrap() { + crossterm::execute!(stdout(), Clear(ClearType::CurrentLine), MoveToColumn(0)).unwrap(); + println!("{}", format_args!($($arg)*)); + crossterm::execute!(stdout(), Clear(ClearType::CurrentLine), MoveToColumn(0)).unwrap(); + } + }}; +} fn main() { let args = Args::parse(); + VERBOSE.set(args.verbose).unwrap(); std::thread::spawn(move || { monolib::run( &args.address, args.xor_key_file.map(|key| std::fs::read(key).expect("Failed to read preshared key")), + Settings { + encoder: Encoder::PcmFloat, + cover: -1, + }, ) }); while monolib::get_metadata().is_none() {} let mut md = monolib::get_metadata().unwrap(); + verbose!("md: {:?}", md); let mut track_start = Instant::now(); let mut seconds_past = 0; crossterm::execute!( @@ -101,9 +123,10 @@ fn main() { } } if monolib::get_metadata().unwrap() != md - && track_length <= (Instant::now() - track_start).as_secs_f64() + //&& track_length <= (Instant::now() - track_start).as_secs_f64() { md = next_md.clone(); + verbose!("md: {:?}", md); crossterm::execute!(stdout(), Clear(ClearType::CurrentLine), MoveToColumn(0)).unwrap(); print!( "Playing: {} - {} - {} (0:00 / {}:{:02})", diff --git a/monolib/Cargo.toml b/monolib/Cargo.toml index 04c4455..baedf10 100644 --- a/monolib/Cargo.toml +++ b/monolib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "monolib" -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT" description = "A library implementing the lonely radio audio streaming protocol" @@ -9,11 +9,19 @@ authors = ["Ivan Bushchik "] [lib] name = "monolib" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["cdylib", "staticlib", "rlib"] [dependencies] rodio = { version = "0.17.3", default-features = false } byteorder = "1.5.0" rmp-serde = "1.1.2" -lonelyradio_types = { version = "0.5.0", path = "../lonelyradio_types" } +lonelyradio_types = { version = "0.6.0", path = "../lonelyradio_types" } claxon = "0.4.3" + +[package.metadata.xcframework] +include-dir = "src" +lib-type = "cdylib" +zip = false +macOS = true +iOS = true +simulators = true diff --git a/monolib/cbindgen.toml b/monolib/cbindgen.toml new file mode 100644 index 0000000..34248ee --- /dev/null +++ b/monolib/cbindgen.toml @@ -0,0 +1,12 @@ + +language = "C" + + +include_version = false + +############################ Code Style Options ################################ + +braces = "SameLine" +line_length = 100 +tab_width = 2 +line_endings = "LF" diff --git a/monolib/src/c.rs b/monolib/src/c.rs index e4791b8..adee145 100644 --- a/monolib/src/c.rs +++ b/monolib/src/c.rs @@ -1,18 +1,46 @@ use crate::*; -use std::ffi::{c_char, c_float, c_ushort}; +use std::ffi::{c_char, c_float}; use std::ffi::{CStr, CString}; +#[repr(C)] +#[derive(Debug, PartialEq)] +pub struct CTrackMetadata { + pub title: *mut c_char, + pub album: *mut c_char, + pub artist: *mut c_char, +} + +pub const ENCODER_PCM16: u8 = 0; +pub const ENCODER_PCMFLOAT: u8 = 1; +pub const ENCODER_FLAC: u8 = 2; + +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub struct CSettings { + /// See lonelyradio_types -> Encoder + pub encoder: u8, + pub cover: i32, +} + #[no_mangle] #[allow(clippy::not_unsafe_ptr_arg_deref)] -pub extern "C" fn c_start(server: *const c_char) { +pub extern "C" fn c_start(server: *const c_char, settings: CSettings) { let serv = unsafe { CStr::from_ptr(server) }; run( match serv.to_str() { Ok(s) => s, _ => "", }, - None, + Settings { + encoder: match settings.encoder { + 0 => Encoder::Pcm16, + 1 => Encoder::PcmFloat, + 2 => Encoder::Flac, + _ => return, + }, + cover: settings.cover, + }, ) } @@ -27,9 +55,9 @@ pub extern "C" fn c_stop() { } #[no_mangle] -pub extern "C" fn c_get_state() -> c_ushort { +pub extern "C" fn c_get_state() -> c_char { let state = STATE.read().unwrap(); - *state as c_ushort + *state as c_char } #[no_mangle] @@ -69,10 +97,55 @@ pub extern "C" fn c_get_metadata_title() -> *mut c_char { } #[no_mangle] -pub extern "C" fn c_get_metadata_length() -> *mut c_float { +pub extern "C" fn c_get_metadata_length() -> c_float { let md = MD.read().unwrap(); match md.as_ref() { - Some(md) => &mut (md.track_length_secs as c_float + md.track_length_frac as c_float), - None => &mut 0.0, + Some(md) => md.track_length_secs as c_float + md.track_length_frac as c_float, + None => 0.0, } } + +#[repr(C)] +pub struct CImageJpeg { + pub length: u32, + pub bytes: *mut u8, +} + +/// # Safety +/// Manually deallocate returned memory after use +#[no_mangle] +pub unsafe extern "C" fn c_get_cover_jpeg() -> CImageJpeg { + let md = MD.read().unwrap(); + if let Some(md) = md.as_ref() { + if let Some(cov) = md.cover.as_ref() { + //eprintln!("{} {:p}", *len, cov.as_ptr()); + let len = cov.len() as u32; + //let b = Box::new(.as_slice()); + let clone = cov.clone(); + let ptr = clone.as_ptr() as *mut u8; + std::mem::forget(clone); + CImageJpeg { + length: len, + bytes: ptr, + } + } else { + eprintln!("No cov"); + CImageJpeg { + length: 0, + bytes: std::ptr::null_mut(), + } + } + } else { + eprintln!("No md"); + CImageJpeg { + length: 0, + bytes: std::ptr::null_mut(), + } + } +} +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn c_drop(ptr: *mut u8, count: usize) { + std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(count, 1).unwrap()); +} diff --git a/monolib/src/lib.rs b/monolib/src/lib.rs index 7a62743..1b9b0a9 100644 --- a/monolib/src/lib.rs +++ b/monolib/src/lib.rs @@ -16,20 +16,20 @@ /// Functions, providing C-like API pub mod c; -mod reader; -use byteorder::{LittleEndian, ReadBytesExt}; -use lonelyradio_types::{Message, TrackMetadata}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use lonelyradio_types::{Encoder, Message, ServerCapabilities, Settings, TrackMetadata}; use rodio::buffer::SamplesBuffer; use rodio::{OutputStream, Sink}; use std::error::Error; -use std::io::{BufReader, Read}; +use std::io::{Read, Write}; use std::net::TcpStream; use std::sync::atomic::AtomicU8; use std::sync::RwLock; use std::time::Instant; -const CACHE_SIZE: usize = 128; +const CACHE_SIZE_PCM: usize = 32; +const CACHE_SIZE_COMPRESSED: usize = 2; static SINK: RwLock> = RwLock::new(None); static VOLUME: AtomicU8 = AtomicU8::new(255); @@ -76,7 +76,8 @@ pub fn stop() { let sink = SINK.read().unwrap(); if let Some(sink) = sink.as_ref() { - sink.pause() + sink.pause(); + sink.clear() } drop(sink); drop(state); @@ -143,12 +144,16 @@ pub fn set_volume(volume: u8) { } /// Download track as samples -pub fn get_track(server: &str, xor_key: Option>) -> Option<(TrackMetadata, Vec)> { - let mut stream = BufReader::new(match xor_key { - Some(k) => reader::Reader::XorEncrypted(TcpStream::connect(server).unwrap(), k, 0), - None => reader::Reader::Unencrypted(TcpStream::connect(server).unwrap()), - }); +pub fn get_track(server: &str, mut settings: Settings) -> Option<(TrackMetadata, Vec)> { + let mut connection = unwrap(TcpStream::connect(server))?; + unwrap(connection.write_u64::(lonelyradio_types::HELLO_MAGIC))?; + let capabilities: ServerCapabilities = unwrap(rmp_serde::from_read(&mut connection))?; + if !capabilities.encoders.contains(&settings.encoder) { + settings.encoder = Encoder::Pcm16 + } + unwrap(connection.write_all(&rmp_serde::to_vec_named(&settings).unwrap()))?; + let mut stream = connection; let mut samples = vec![]; let mut md: Option = None; loop { @@ -160,47 +165,59 @@ pub fn get_track(server: &str, xor_key: Option>) -> Option<(TrackMetadat } md = Some(tmd); } - Message::F(fmd) => { - if !md.clone().unwrap().flac { + Message::F(fmd) => match md.as_ref().unwrap().encoder { + Encoder::Pcm16 => { let mut buf = vec![0; fmd.length as usize]; stream.read_i16_into::(&mut buf).unwrap(); samples.append(&mut buf); - } else { - let take = stream.by_ref().take(fmd.length); + } + Encoder::PcmFloat => unimplemented!(), + Encoder::Flac => { + let take = std::io::Read::by_ref(&mut stream).take(fmd.length); let mut reader = claxon::FlacReader::new(take).unwrap(); samples.append( &mut reader.samples().map(|x| x.unwrap_or(0) as i16).collect::>(), ); } - } + }, } } md.map(|md| (md, samples)) } -fn unwrap(thing: Result) -> T { +fn unwrap(thing: Result) -> Option { if thing.is_err() { *STATE.write().unwrap() = State::NotStarted; } - thing.unwrap() + thing.ok() } /// Starts playing at "server:port" -pub fn run(server: &str, xor_key: Option>) { +pub fn run(server: &str, settings: Settings) { + let _ = _run(server, settings); +} + +pub fn _run(server: &str, settings: Settings) -> Option<()> { + let mut settings = settings; let mut state = STATE.write().unwrap(); if *state == State::Playing || *state == State::Paused { - return; + return None; } *state = State::Playing; drop(state); - let mut stream = BufReader::new(match xor_key { - Some(k) => reader::Reader::XorEncrypted(unwrap(TcpStream::connect(server)), k, 0), - None => reader::Reader::Unencrypted(unwrap(TcpStream::connect(server))), - }); + let mut connection = unwrap(TcpStream::connect(server))?; + unwrap(connection.write_u64::(lonelyradio_types::HELLO_MAGIC))?; + let capabilities: ServerCapabilities = unwrap(rmp_serde::from_read(&mut connection))?; + if !capabilities.encoders.contains(&settings.encoder) { + settings.encoder = Encoder::Pcm16 + } + unwrap(connection.write_all(&rmp_serde::to_vec_named(&settings).unwrap()))?; + + let mut stream = connection; let mut sink = SINK.write().unwrap(); - let (_stream, stream_handle) = unwrap(OutputStream::try_default()); + let (_stream, stream_handle) = unwrap(OutputStream::try_default())?; // Can't reuse old sink for some reason let audio_sink = Sink::try_new(&stream_handle).unwrap(); @@ -215,7 +232,7 @@ pub fn run(server: &str, xor_key: Option>) { // No metadata shift if watching_sleep_until_end() { _stop(); - return; + return None; } let mut md = MD.write().unwrap(); *md = Some(tmd.clone()); @@ -227,49 +244,60 @@ pub fn run(server: &str, xor_key: Option>) { } if *STATE.read().unwrap() == State::Resetting { _stop(); - return; - } - if !MD.read().unwrap().clone().unwrap().flac { - let mut samples_i16 = vec![0; fmd.length as usize]; - if stream.read_i16_into::(&mut samples_i16).is_err() { - return; - }; - samples.append( - &mut samples_i16.iter().map(|sample| *sample as f32 / 32767.0).collect(), - ); - } else { - let take = stream.by_ref().take(fmd.length); - let mut reader = claxon::FlacReader::new(take).unwrap(); - samples.append( - &mut reader - .samples() - .map(|x| x.unwrap_or(0) as f32 / 32767.0) - .collect::>(), - ); + return None; } + match MD.read().unwrap().as_ref().unwrap().encoder { + Encoder::Pcm16 => { + let mut samples_i16 = vec![0; fmd.length as usize / 2]; + if stream.read_i16_into::(&mut samples_i16).is_err() { + return None; + }; + samples.append( + &mut samples_i16 + .iter() + .map(|sample| *sample as f32 / 32767.0) + .collect(), + ); + } + Encoder::PcmFloat => { + let mut samples_f32 = vec![0f32; fmd.length as usize / 4]; + if stream.read_f32_into::(&mut samples_f32).is_err() { + return None; + }; + samples.append(&mut samples_f32); + } + Encoder::Flac => { + let take = std::io::Read::by_ref(&mut stream).take(fmd.length); + let mut reader = claxon::FlacReader::new(take).unwrap(); + samples.append( + &mut reader + .samples() + .map(|x| x.unwrap_or(0) as f32 / 32768.0 / 256.0) + .collect::>(), + ); + } + }; - // Sink's thread is detached from main thread, so we need to synchronize with it - // Why we should synchronize with it? - // Let's say, that if we don't synchronize with it, we would have - // a lot (no upper limit, actualy) of buffered sound, waiting for playing in - // sink + // Synchronizing with sink let sink = SINK.read().unwrap(); let _md = MD.read().unwrap(); let md = _md.as_ref().unwrap().clone(); drop(_md); if let Some(sink) = sink.as_ref() { - while sink.len() >= CACHE_SIZE { + while (sink.len() >= CACHE_SIZE_PCM && md.encoder != Encoder::Flac) + || (sink.len() >= CACHE_SIZE_COMPRESSED && md.encoder == Encoder::Flac) + { // Sleeping exactly one buffer and watching for reset signal if watching_sleep( if sink.len() > 2 { sink.len() as f32 - 2.0 } else { 0.25 - } * fmd.length as f32 / md.sample_rate as f32 + } * samples.len() as f32 / md.sample_rate as f32 / 4.0, ) { _stop(); - return; + return None; } } sink.append(SamplesBuffer::new( diff --git a/monolib/src/module.modulemap b/monolib/src/module.modulemap new file mode 100644 index 0000000..e54feff --- /dev/null +++ b/monolib/src/module.modulemap @@ -0,0 +1,5 @@ +framework module MonoLib { + // a header file in the same directory as the modulemap + header "monolib.h" + export * +} diff --git a/monolib/src/monolib.h b/monolib/src/monolib.h index 34bd411..eaeadaa 100644 --- a/monolib/src/monolib.h +++ b/monolib/src/monolib.h @@ -1,20 +1,42 @@ #include #include +#include #include #include -void c_start(const char *server); +typedef struct CImageJpeg { + uint32_t length; + uint8_t *bytes; +} CImageJpeg; -void c_toggle(void); +typedef struct CSettings { + /** + * See lonelyradio_types -> Encoder + */ + uint8_t encoder; + int32_t cover; +} CSettings; -void c_stop(void); +void c_drop(uint8_t *ptr, size_t count); -unsigned short c_get_state(void); - -char *c_get_metadata_artist(void); +/** + * # Safety + * Manually deallocate returned memory after use + */ +struct CImageJpeg c_get_cover_jpeg(void); char *c_get_metadata_album(void); +char *c_get_metadata_artist(void); + +float c_get_metadata_length(void); + char *c_get_metadata_title(void); -float *c_get_metadata_length(void); +char c_get_state(void); + +void c_start(const char *server, struct CSettings settings); + +void c_stop(void); + +void c_toggle(void); diff --git a/monolib/src/reader.rs b/monolib/src/reader.rs deleted file mode 100644 index 9102935..0000000 --- a/monolib/src/reader.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{io, net::TcpStream}; - -pub(crate) enum Reader { - Unencrypted(TcpStream), - XorEncrypted(TcpStream, Vec, u64), -} - -impl io::Read for Reader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self { - Self::Unencrypted(s) => s.read(buf), - Self::XorEncrypted(s, key, n) => { - let out = s.read(buf); - if let Ok(i) = &out { - for k in buf.iter_mut().take(*i) { - *k ^= key[*n as usize]; - *n += 1; - *n %= key.len() as u64; - } - } - out - } - } - } -} diff --git a/monolib/src/target b/monolib/src/target new file mode 120000 index 0000000..78bc337 --- /dev/null +++ b/monolib/src/target @@ -0,0 +1 @@ +../target \ No newline at end of file diff --git a/monolib/target b/monolib/target new file mode 120000 index 0000000..78bc337 --- /dev/null +++ b/monolib/target @@ -0,0 +1 @@ +../target \ No newline at end of file diff --git a/monoloader/Cargo.toml b/monoloader/Cargo.toml index 8b9a223..1fa6af0 100644 --- a/monoloader/Cargo.toml +++ b/monoloader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "monoloader" -version = "0.4.0" +version = "0.6.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,3 +9,4 @@ edition = "2021" monolib = { path = "../monolib" } clap = { version = "4.4.18", features = ["derive"] } hound = "3.5.1" +lonelyradio_types = { version = "0.6.0", path = "../lonelyradio_types" } diff --git a/monoloader/src/main.rs b/monoloader/src/main.rs index 0bc17cd..7c38b06 100644 --- a/monoloader/src/main.rs +++ b/monoloader/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use lonelyradio_types::Settings; use std::path::PathBuf; #[derive(Parser)] @@ -14,7 +15,10 @@ fn main() { let args = Args::parse(); let (md, samples) = monolib::get_track( &args.address, - args.xor_key_file.map(|key| std::fs::read(key).expect("Failed to read preshared key")), + Settings { + encoder: lonelyradio_types::Encoder::Pcm16, + cover: -1, + }, ) .unwrap(); println!( diff --git a/platform/screenshots/swiftui/iphone1.png b/platform/screenshots/swiftui/iphone1.png deleted file mode 100644 index c1e4c41..0000000 Binary files a/platform/screenshots/swiftui/iphone1.png and /dev/null differ diff --git a/platform/screenshots/swiftui/iphone2.png b/platform/screenshots/swiftui/iphone2.png deleted file mode 100644 index 5f2ec61..0000000 Binary files a/platform/screenshots/swiftui/iphone2.png and /dev/null differ diff --git a/platform/screenshots/swiftui/macos_dark.png b/platform/screenshots/swiftui/macos_dark.png deleted file mode 100644 index 4e99b84..0000000 Binary files a/platform/screenshots/swiftui/macos_dark.png and /dev/null differ diff --git a/platform/screenshots/swiftui/macos_light.png b/platform/screenshots/swiftui/macos_light.png deleted file mode 100644 index 92d64da..0000000 Binary files a/platform/screenshots/swiftui/macos_light.png and /dev/null differ diff --git a/platform/swiftui/MonoLib-Bridging-Header.h b/platform/swiftui/MonoLib-Bridging-Header.h deleted file mode 100644 index cad221a..0000000 --- a/platform/swiftui/MonoLib-Bridging-Header.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// MonoLib-Bridging-Header.h -// monoclient -// -// Created by ivabus on 03.03.2024. -// - -#ifndef MonoLib_Bridging_Header_h -#define MonoLib_Bridging_Header_h - -#import "monolib.h" - -#endif /* MonoLib_Bridging_Header_h */ diff --git a/platform/swiftui/README.md b/platform/swiftui/README.md deleted file mode 100644 index 3929e03..0000000 --- a/platform/swiftui/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Platform-specific player realizations - -## Rust + SwiftUI (iOS/iPadOS/macOS (iOS mode)) - -### Build `monolib` - -``` -cargo lipo --release --targets aarch64-apple-ios -p monolib -``` - -For running in simulator - -``` -cargo lipo --release --targets aarch64-apple-ios-sim,x86_64-apple-ios -p monolib -``` - -### Build and run app - -Open Xcode and run. - -[Screenshots (pre v0.2)](./screenshots/swiftui) diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-1024@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-1024@1x.png deleted file mode 100644 index 69da806..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-1024@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png deleted file mode 100644 index e111ebb..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png deleted file mode 100644 index 8b3dc2b..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-128@2x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png deleted file mode 100644 index 755d55a..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png deleted file mode 100644 index d4c5813..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-16@2x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png deleted file mode 100644 index 8b3dc2b..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png deleted file mode 100644 index 246e82b..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png deleted file mode 100644 index d4c5813..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png deleted file mode 100644 index 01edb8b..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-32@2x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@1x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@1x.png deleted file mode 100644 index 246e82b..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@1x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@2x.png b/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@2x.png deleted file mode 100644 index 69da806..0000000 Binary files a/platform/swiftui/monoclient/Assets.xcassets/AppIcon.appiconset/icon-512@2x.png and /dev/null differ diff --git a/platform/swiftui/monoclient/ContentView.swift b/platform/swiftui/monoclient/ContentView.swift deleted file mode 100644 index 234fdf6..0000000 --- a/platform/swiftui/monoclient/ContentView.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// ContentView.swift -// monoclient -// -// Created by ivabus on 03.03.2024. -// - -import AVFAudio -import SwiftUI - -class MonoLib { - func run(server: String) async { - let audioSession = AVAudioSession.sharedInstance() - do { - try audioSession.setCategory( - .playback, mode: .default, - policy: .longFormAudio) - try audioSession.setActive(true) - - } catch { - print("Failed to set the audio session configuration") - } - c_start(server) - } -} - -struct ContentView: View { - let timer = Timer.publish(every: 0.25, on: .main, in: .common).autoconnect() - @State private var server: String = "" - @State private var port: String = "" - @State private var playing: Bool = true - @State private var running: Bool = false - - @State var now_playing_artist: String = "" - @State var now_playing_album: String = "" - @State var now_playing_title: String = "" - - var body: some View { - VStack { - Text("Monoclient").font(.largeTitle).fontWidth(.expanded).bold() - VStack(alignment: .center) { - HStack { - Text("Server").frame(minWidth: 50, idealWidth: 60) - TextField( - "Required", - text: $server - ) - .disableAutocorrection(true) - - } - .textFieldStyle(.roundedBorder) - HStack { - Text("Port").frame(minWidth: 50, idealWidth: 60) - TextField( - "Required", - text: $port - ) - .disableAutocorrection(true).keyboardType(.numberPad).keyboardShortcut(.escape) - } - .textFieldStyle(.roundedBorder) - - Button(action: { - if running { - playing = !playing - c_toggle() - } - running = true - let a = MonoLib() - Task.init { - await a.run(server: server + ":" + port) - } - }) { - Image( - systemName: running - ? (playing ? "pause.circle.fill" : "play.circle") : "infinity.circle" - ).font(.largeTitle) - }.buttonStyle( - .borderedProminent) - HStack{ - Button(action: { - c_stop() - running = false - playing = true - }) { Image(systemName: "stop").font(.title3) }.buttonStyle( - .bordered - ).disabled(!running) - Button(action: { - c_stop() - playing = true - let a = MonoLib() - Task.init { - await a.run(server: server + ":" + port) - } - }) {Image(systemName: "forward").font(.title3)}.buttonStyle(.bordered).disabled(!running) - } - }.frame(width: 300) - VStack(spacing: 10) { - Text(now_playing_artist).onReceive(timer) { _ in - now_playing_artist = String(cString: c_get_metadata_artist()!) - } - Text(now_playing_album).onReceive(timer) { _ in - now_playing_album = String(cString: c_get_metadata_album()!) - } - Text(now_playing_title).onReceive(timer) { _ in - now_playing_title = String(cString: c_get_metadata_title()!) - }.bold() - }.frame(minHeight: 100) - - }.padding() - - } -} - -#Preview { - ContentView() -} diff --git a/platform/swiftui/monoclient/monoclientApp.swift b/platform/swiftui/monoclient/monoclientApp.swift deleted file mode 100644 index d69f3ca..0000000 --- a/platform/swiftui/monoclient/monoclientApp.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// monoclientApp.swift -// monoclient -// -// Created by ivabus on 03.03.2024. -// - -import SwiftUI - -@main -struct monoclientApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} diff --git a/src/decode.rs b/src/decode.rs index 1cd48af..7d2804e 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -65,19 +65,11 @@ pub async fn get_meta(file_path: &Path) -> (u16, u32, Time) { } let args = Args::parse(); - ( - channels, - if sample_rate > args.max_samplerate { - args.max_samplerate - } else { - sample_rate - }, - track_length, - ) + (channels, get_resampling_rate(&sample_rate, &args.max_samplerate), track_length) } /// Getting samples -pub fn decode_file_stream(file_path: PathBuf) -> impl Stream> { +pub fn decode_file_stream(file_path: PathBuf) -> impl Stream> { let args = Args::parse(); let file = Box::new(std::fs::File::open(&file_path).unwrap()); let mut hint = Hint::new(); @@ -122,13 +114,13 @@ pub fn decode_file_stream(file_path: PathBuf) -> impl Stream> { let mut byte_buf = SampleBuffer::::new(decoded.capacity() as u64, *decoded.spec()); byte_buf.copy_interleaved_ref(decoded); - let output_rate = get_resampling_rate(&spec.rate, &args.max_samplerate); + let output_rate = get_resampling_rate(&spec.rate,&args.max_samplerate); // About Samplerate struct: // We are downsampling, not upsampling, so we should be fine yield ( if output_rate == spec.rate { - byte_buf.samples().iter().map(|x| (*x * 32768.0) as i16).collect() + byte_buf.samples().to_vec() } else { samplerate::convert( spec.rate, @@ -138,17 +130,14 @@ pub fn decode_file_stream(file_path: PathBuf) -> impl Stream> { byte_buf.samples(), ) .unwrap() - .iter() - .map(|x| (*x * 32768.0) as i16) - .collect() } ); } else { let mut byte_buf = - SampleBuffer::::new(decoded.capacity() as u64, *decoded.spec()); + SampleBuffer::::new(decoded.capacity() as u64, *decoded.spec()); byte_buf.copy_interleaved_ref(decoded); - yield (byte_buf.samples().to_vec()); + yield byte_buf.samples().to_vec(); } continue; } diff --git a/src/encode.rs b/src/encode.rs new file mode 100644 index 0000000..0d8e019 --- /dev/null +++ b/src/encode.rs @@ -0,0 +1,52 @@ +use flacenc::{component::BitRepr, error::Verify, source::MemSource}; +use lonelyradio_types::Encoder; + +pub fn encode( + codec: Encoder, + mut samples: Vec, + sample_rate: u32, + channels: u16, +) -> Option> { + match codec { + Encoder::Pcm16 => { + let mut samples = samples.iter_mut().map(|x| (*x * 32768.0) as i16).collect::>(); + // Launching lonelyradio on the router moment + if cfg!(target_endian = "big") { + samples.iter_mut().for_each(|sample| { + *sample = sample.to_le(); + }); + } + // Sowwy about that + let (_, samples, _) = unsafe { samples.align_to::() }; + Some(samples.to_vec()) + } + Encoder::PcmFloat => { + // Launching lonelyradio on the router moment + // Sowwy about that + let samples = samples.iter().map(|x| x.to_bits()).collect::>(); + let (_, samples, _) = unsafe { samples.align_to::() }; + Some(samples.to_vec()) + } + Encoder::Flac => { + let encoded = flacenc::encode_with_fixed_block_size( + &flacenc::config::Encoder::default().into_verified().unwrap(), + MemSource::from_samples( + // I'm crying (It's just a burning memory) + &samples + .iter() + .map(|x| (*x as f64 * 32768.0 * 256.0) as i32) + .collect::>(), + channels as usize, + 24, + sample_rate as usize, + ), + 256, + ) + .unwrap(); + + let mut sink = flacenc::bitsink::ByteSink::new(); + encoded.write(&mut sink).unwrap(); + Some(sink.as_slice().to_vec()) + } + } +} diff --git a/src/main.rs b/src/main.rs index 4556511..284fb2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,30 @@ mod decode; -mod writer; +mod encode; +use std::io::Cursor; +use std::io::Read; +use std::net::TcpStream; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use chrono::Local; use clap::Parser; -use flacenc::component::BitRepr; -use flacenc::error::Verify; -use flacenc::source::MemSource; +use encode::encode; use futures_util::pin_mut; use futures_util::StreamExt; +use image::io::Reader as ImageReader; use lofty::Accessor; use lofty::TaggedFileExt; +use lonelyradio_types::Encoder; +use lonelyradio_types::ServerCapabilities; +use lonelyradio_types::Settings; use lonelyradio_types::{FragmentMetadata, Message, TrackMetadata}; -use once_cell::sync::Lazy; use rand::prelude::*; use std::io::Write; use tokio::net::TcpListener; use tokio_stream::Stream; use walkdir::DirEntry; -use writer::Writer; use crate::decode::decode_file_stream; use crate::decode::get_meta; @@ -51,48 +54,37 @@ struct Args { #[arg(long)] no_resampling: bool, - /// Use FLAC compression - #[arg(short, long)] - flac: bool, - - /// Enable XOR "encryption" - #[arg(long)] - xor_key_file: Option, + /// Size of artwork (-1 for no artwork, 0 for original, N for NxN) + #[arg(long, default_value = "96000")] + artwork: i32, } -static KEY: Lazy>>> = Lazy::new(|| { - let args = Args::parse(); - if let Some(path) = args.xor_key_file { - let key = std::fs::read(path).expect("Failed to read preshared key"); - Some(Arc::new(key)) - } else { - None - } -}); +const SUPPORTED_ENCODERS: [Encoder; 3] = [Encoder::Pcm16, Encoder::PcmFloat, Encoder::Flac]; async fn stream_track( - samples_stream: impl Stream>, + samples_stream: impl Stream>, war: bool, md: TrackMetadata, - s: &mut Writer, + s: &mut TcpStream, ) -> bool { pin_mut!(samples_stream); let _md = md.clone(); - if s.write_all(rmp_serde::to_vec(&Message::T(_md)).unwrap().as_slice()).is_err() { + if s.write_all(rmp_serde::encode::to_vec_named(&Message::T(_md)).unwrap().as_slice()).is_err() { return true; }; // Why chunks? - // flacenc is broken on low amount of samples (Symphonia's AIFF decoder returns ~2304 - // samples per packet (on bo en's tracks), instead of usual ~8192 on any other lossless decoder) + // flacenc is broken on low amount of samples (Symphonia's AIFF decoder returns + // ~2304 samples per packet (on bo en's tracks), instead of usual ~8192 on any + // other lossless decoder) while let Some(mut _samples) = samples_stream .as_mut() - .chunks(if md.flac && md.track_length_secs > 1 { - 2 - } else { - 1 + .chunks(match md.encoder { + Encoder::Pcm16 => 1, + Encoder::PcmFloat => 1, + Encoder::Flac => 16, }) .next() .await @@ -100,57 +92,51 @@ async fn stream_track( let mut _samples = _samples.concat(); if war { _samples.iter_mut().for_each(|sample| { - *sample = sample.signum() * 32767; + *sample = sample.signum(); }); } - if !md.flac { - let _md = Message::F(FragmentMetadata { - length: _samples.len() as u64, - }); - if s.write_all(rmp_serde::to_vec(&_md).unwrap().as_slice()).is_err() { - return true; - } - - // Launching lonelyradio on the router moment - if cfg!(target_endian = "big") { - _samples.iter_mut().for_each(|sample| { - *sample = sample.to_le(); + match md.encoder { + Encoder::Pcm16 => { + let _md = Message::F(FragmentMetadata { + length: _samples.len() as u64 * 2, }); + if s.write_all(rmp_serde::to_vec(&_md).unwrap().as_slice()).is_err() { + return true; + } + if s.write_all( + &encode(Encoder::Pcm16, _samples, md.sample_rate, md.channels).unwrap(), + ) + .is_err() + { + return true; + } } - - // Sowwy about that - let (_, samples, _) = unsafe { _samples.align_to::() }; - - if s.write_all(samples).is_err() { - return true; + Encoder::PcmFloat => { + let _md = Message::F(FragmentMetadata { + length: _samples.len() as u64 * 4, + }); + if s.write_all(rmp_serde::to_vec(&_md).unwrap().as_slice()).is_err() { + return true; + } + if s.write_all( + &encode(Encoder::PcmFloat, _samples, md.sample_rate, md.channels).unwrap(), + ) + .is_err() + { + return true; + } } - } else { - let encoded = flacenc::encode_with_fixed_block_size( - &flacenc::config::Encoder::default().into_verified().unwrap(), - MemSource::from_samples( - // I'm crying (It's just a burning memory) - &_samples.iter().map(|x| *x as i32).collect::>(), - md.channels as usize, - 16, - md.sample_rate as usize, - ), - 256, - ); - if encoded.is_err() { - return true; - } - - let mut sink = flacenc::bitsink::ByteSink::new(); - encoded.unwrap().write(&mut sink).unwrap(); - - let _md = Message::F(FragmentMetadata { - length: sink.as_slice().len() as u64, - }); - if s.write_all(rmp_serde::to_vec(&_md).unwrap().as_slice()).is_err() { - return true; - } - if s.write_all(sink.as_slice()).is_err() { - return true; + Encoder::Flac => { + let encoded = encode(Encoder::Flac, _samples, md.sample_rate, md.channels).unwrap(); + let _md = Message::F(FragmentMetadata { + length: encoded.as_slice().len() as u64, + }); + if s.write_all(rmp_serde::to_vec(&_md).unwrap().as_slice()).is_err() { + return true; + } + if s.write_all(encoded.as_slice()).is_err() { + return true; + } } } } @@ -160,9 +146,9 @@ async fn stream_track( #[tokio::main] async fn main() { let args = Args::parse(); - let listener = TcpListener::bind(Args::parse().address).await.unwrap(); + let listener = TcpListener::bind(args.address).await.unwrap(); let tracklist = Arc::new( - walkdir::WalkDir::new(Args::parse().dir) + walkdir::WalkDir::new(args.dir) .into_iter() .filter_entry(is_not_hidden) .filter_map(|v| v.ok()) @@ -172,25 +158,36 @@ async fn main() { ); loop { let (socket, _) = listener.accept().await.unwrap(); - let s = socket.into_std().unwrap(); + let mut s = socket.into_std().unwrap(); s.set_nonblocking(false).unwrap(); - let s = if args.xor_key_file.is_some() { - Writer::XorEncrypted( - s, - match &*KEY { - Some(a) => a.clone(), - _ => { - unreachable!() - } - }, - 0, - ) - } else { - Writer::Unencrypted(s) + let mut hello = [0u8; 8]; + if s.read_exact(&mut hello).is_err() { + continue; + } + if hello != lonelyradio_types::HELLO_MAGIC.to_le_bytes() { + continue; + } + if s.write_all( + &rmp_serde::to_vec_named(&ServerCapabilities { + encoders: SUPPORTED_ENCODERS.to_vec(), + }) + .unwrap(), + ) + .is_err() + { + continue; }; - tokio::spawn(stream(s, tracklist.clone())); + let settings: Settings = match rmp_serde::from_read(&s) { + Ok(s) => s, + _ => continue, + }; + if settings.cover < -1 { + continue; + } + tokio::spawn(stream(s, tracklist.clone(), settings)); } } + fn is_not_hidden(entry: &DirEntry) -> bool { entry.file_name().to_str().map(|s| entry.depth() == 0 || !s.starts_with('.')).unwrap_or(false) } @@ -207,7 +204,7 @@ fn track_valid(track: &Path) -> bool { true } -async fn stream(mut s: Writer, tracklist: Arc>) { +async fn stream(mut s: TcpStream, tracklist: Arc>, settings: Settings) { let args = Args::parse(); loop { @@ -216,6 +213,7 @@ async fn stream(mut s: Writer, tracklist: Arc>) { let mut title = String::new(); let mut artist = String::new(); let mut album = String::new(); + let mut cover = std::thread::spawn(|| None); let mut file = std::fs::File::open(&track).unwrap(); let tagged = match lofty::read_from(&mut file) { Ok(f) => f, @@ -224,12 +222,33 @@ async fn stream(mut s: Writer, tracklist: Arc>) { if let Some(id3v2) = tagged.primary_tag() { title = id3v2.title().unwrap_or(track.file_stem().unwrap().to_string_lossy()).to_string(); - album = id3v2.album().unwrap_or("[No tag]".into()).to_string(); - artist = id3v2.artist().unwrap_or("[No tag]".into()).to_string(); + album = id3v2.album().unwrap_or("".into()).to_string(); + artist = id3v2.artist().unwrap_or("".into()).to_string(); + if !(id3v2.pictures().is_empty() || args.artwork == -1 || settings.cover == -1) { + let pic = id3v2.pictures()[0].clone(); + cover = std::thread::spawn(move || { + let dec = ImageReader::new(Cursor::new(pic.into_data())) + .with_guessed_format() + .ok()? + .decode() + .ok()?; + let mut img = Vec::new(); + if args.artwork != 0 && settings.cover != 0 { + let size = std::cmp::min(args.artwork as u32, settings.cover as u32); + dec.resize(size, size, image::imageops::FilterType::Lanczos3) + } else { + dec + } + .to_rgb8() + .write_to(&mut Cursor::new(&mut img), image::ImageFormat::Jpeg) + .unwrap(); + Some(img) + }); + }; }; let track_message = format!("{} - {} - {}", &artist, &album, &title); eprintln!( - "[{}] {} to {}:{}{}", + "[{}] {} to {}:{}{} ({:?})", Local::now().to_rfc3339(), track_message, s.peer_addr().unwrap().ip(), @@ -238,7 +257,8 @@ async fn stream(mut s: Writer, tracklist: Arc>) { " with WAR.rs" } else { "" - } + }, + settings.encoder ); if args.public_log { @@ -262,7 +282,8 @@ async fn stream(mut s: Writer, tracklist: Arc>) { TrackMetadata { track_length_frac: time.frac as f32, track_length_secs: time.seconds, - flac: args.flac, + encoder: settings.encoder, + cover: cover.join().unwrap(), album, artist, title, diff --git a/src/writer.rs b/src/writer.rs deleted file mode 100644 index e1cd17a..0000000 --- a/src/writer.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::{ - borrow::BorrowMut, - io, - net::{SocketAddr, TcpStream}, - sync::Arc, -}; - -pub(crate) enum Writer { - Unencrypted(TcpStream), - XorEncrypted(TcpStream, Arc>, u64), -} - -impl io::Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self { - Self::Unencrypted(s) => s.write(buf), - Self::XorEncrypted(s, key, n) => { - for mut k in buf.iter().copied() { - k ^= key[*n as usize]; - *n += 1; - *n %= key.len() as u64; - s.write_all(&[k])?; - } - Ok(buf.len()) - } - } - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - match self { - Self::Unencrypted(s) => s.write_all(buf), - Self::XorEncrypted(s, key, n) => s.write_all( - &buf.iter() - .borrow_mut() - .copied() - .map(|mut k| { - k ^= key[*n as usize]; - *n += 1; - *n %= key.len() as u64; - k - }) - // I don't like it - .collect::>(), - ), - } - } - fn flush(&mut self) -> io::Result<()> { - match self { - Self::XorEncrypted(s, _, _) | Self::Unencrypted(s) => s.flush(), - } - } -} - -impl Writer { - pub fn peer_addr(&self) -> io::Result { - match self { - Self::XorEncrypted(s, _, _) | Self::Unencrypted(s) => s.peer_addr(), - } - } -}