Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #58 from tsirysndr/feat/mpris

add MPRIS support

authored by

Tsiry Sandratraina and committed by
GitHub
fe56b854 c21c32db

+929 -33
+1 -1
.github/workflows/release-desktop.yml
··· 22 22 uses: fluentci-io/setup-fluentci@v5 23 23 with: 24 24 wasm: true 25 - pipeline: bun 25 + plugin: bun 26 26 args: | 27 27 run build:electron 28 28 working-directory: webui/rockbox
+520 -9
Cargo.lock
··· 475 475 476 476 [[package]] 477 477 name = "anyhow" 478 - version = "1.0.91" 478 + version = "1.0.93" 479 479 source = "registry+https://github.com/rust-lang/crates.io-index" 480 - checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" 480 + checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" 481 481 482 482 [[package]] 483 483 name = "arc-swap" ··· 561 561 ] 562 562 563 563 [[package]] 564 + name = "async-attributes" 565 + version = "1.1.2" 566 + source = "registry+https://github.com/rust-lang/crates.io-index" 567 + checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" 568 + dependencies = [ 569 + "quote", 570 + "syn 1.0.109", 571 + ] 572 + 573 + [[package]] 574 + name = "async-broadcast" 575 + version = "0.7.1" 576 + source = "registry+https://github.com/rust-lang/crates.io-index" 577 + checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" 578 + dependencies = [ 579 + "event-listener 5.3.1", 580 + "event-listener-strategy", 581 + "futures-core", 582 + "pin-project-lite", 583 + ] 584 + 585 + [[package]] 586 + name = "async-channel" 587 + version = "1.9.0" 588 + source = "registry+https://github.com/rust-lang/crates.io-index" 589 + checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 590 + dependencies = [ 591 + "concurrent-queue", 592 + "event-listener 2.5.3", 593 + "futures-core", 594 + ] 595 + 596 + [[package]] 564 597 name = "async-channel" 565 598 version = "2.3.1" 566 599 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 587 620 ] 588 621 589 622 [[package]] 623 + name = "async-executor" 624 + version = "1.13.1" 625 + source = "registry+https://github.com/rust-lang/crates.io-index" 626 + checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" 627 + dependencies = [ 628 + "async-task", 629 + "concurrent-queue", 630 + "fastrand", 631 + "futures-lite", 632 + "slab", 633 + ] 634 + 635 + [[package]] 636 + name = "async-fs" 637 + version = "2.1.2" 638 + source = "registry+https://github.com/rust-lang/crates.io-index" 639 + checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" 640 + dependencies = [ 641 + "async-lock", 642 + "blocking", 643 + "futures-lite", 644 + ] 645 + 646 + [[package]] 647 + name = "async-global-executor" 648 + version = "2.4.1" 649 + source = "registry+https://github.com/rust-lang/crates.io-index" 650 + checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" 651 + dependencies = [ 652 + "async-channel 2.3.1", 653 + "async-executor", 654 + "async-io", 655 + "async-lock", 656 + "blocking", 657 + "futures-lite", 658 + "once_cell", 659 + ] 660 + 661 + [[package]] 590 662 name = "async-graphql" 591 663 version = "7.0.11" 592 664 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 630 702 "actix-http", 631 703 "actix-web", 632 704 "actix-web-actors", 633 - "async-channel", 705 + "async-channel 2.3.1", 634 706 "async-graphql", 635 707 "async-stream", 636 708 "futures-channel", ··· 681 753 ] 682 754 683 755 [[package]] 756 + name = "async-io" 757 + version = "2.3.4" 758 + source = "registry+https://github.com/rust-lang/crates.io-index" 759 + checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" 760 + dependencies = [ 761 + "async-lock", 762 + "cfg-if", 763 + "concurrent-queue", 764 + "futures-io", 765 + "futures-lite", 766 + "parking", 767 + "polling", 768 + "rustix", 769 + "slab", 770 + "tracing", 771 + "windows-sys 0.59.0", 772 + ] 773 + 774 + [[package]] 775 + name = "async-lock" 776 + version = "3.4.0" 777 + source = "registry+https://github.com/rust-lang/crates.io-index" 778 + checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" 779 + dependencies = [ 780 + "event-listener 5.3.1", 781 + "event-listener-strategy", 782 + "pin-project-lite", 783 + ] 784 + 785 + [[package]] 786 + name = "async-process" 787 + version = "2.3.0" 788 + source = "registry+https://github.com/rust-lang/crates.io-index" 789 + checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" 790 + dependencies = [ 791 + "async-channel 2.3.1", 792 + "async-io", 793 + "async-lock", 794 + "async-signal", 795 + "async-task", 796 + "blocking", 797 + "cfg-if", 798 + "event-listener 5.3.1", 799 + "futures-lite", 800 + "rustix", 801 + "tracing", 802 + ] 803 + 804 + [[package]] 805 + name = "async-recursion" 806 + version = "1.1.1" 807 + source = "registry+https://github.com/rust-lang/crates.io-index" 808 + checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" 809 + dependencies = [ 810 + "proc-macro2", 811 + "quote", 812 + "syn 2.0.85", 813 + ] 814 + 815 + [[package]] 816 + name = "async-signal" 817 + version = "0.2.10" 818 + source = "registry+https://github.com/rust-lang/crates.io-index" 819 + checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" 820 + dependencies = [ 821 + "async-io", 822 + "async-lock", 823 + "atomic-waker", 824 + "cfg-if", 825 + "futures-core", 826 + "futures-io", 827 + "rustix", 828 + "signal-hook-registry", 829 + "slab", 830 + "windows-sys 0.59.0", 831 + ] 832 + 833 + [[package]] 834 + name = "async-std" 835 + version = "1.13.0" 836 + source = "registry+https://github.com/rust-lang/crates.io-index" 837 + checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" 838 + dependencies = [ 839 + "async-attributes", 840 + "async-channel 1.9.0", 841 + "async-global-executor", 842 + "async-io", 843 + "async-lock", 844 + "async-process", 845 + "crossbeam-utils", 846 + "futures-channel", 847 + "futures-core", 848 + "futures-io", 849 + "futures-lite", 850 + "gloo-timers", 851 + "kv-log-macro", 852 + "log", 853 + "memchr", 854 + "once_cell", 855 + "pin-project-lite", 856 + "pin-utils", 857 + "slab", 858 + "wasm-bindgen-futures", 859 + ] 860 + 861 + [[package]] 684 862 name = "async-stream" 685 863 version = "0.3.5" 686 864 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 701 879 "quote", 702 880 "syn 2.0.85", 703 881 ] 882 + 883 + [[package]] 884 + name = "async-task" 885 + version = "4.7.1" 886 + source = "registry+https://github.com/rust-lang/crates.io-index" 887 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 704 888 705 889 [[package]] 706 890 name = "async-trait" ··· 987 1171 ] 988 1172 989 1173 [[package]] 1174 + name = "blocking" 1175 + version = "1.6.1" 1176 + source = "registry+https://github.com/rust-lang/crates.io-index" 1177 + checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 1178 + dependencies = [ 1179 + "async-channel 2.3.1", 1180 + "async-task", 1181 + "futures-io", 1182 + "futures-lite", 1183 + "piper", 1184 + ] 1185 + 1186 + [[package]] 990 1187 name = "brotli" 991 1188 version = "6.0.0" 992 1189 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1109 1306 version = "0.1.1" 1110 1307 source = "registry+https://github.com/rust-lang/crates.io-index" 1111 1308 checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 1309 + 1310 + [[package]] 1311 + name = "cfg_aliases" 1312 + version = "0.2.1" 1313 + source = "registry+https://github.com/rust-lang/crates.io-index" 1314 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 1112 1315 1113 1316 [[package]] 1114 1317 name = "chrono" ··· 2723 2926 ] 2724 2927 2725 2928 [[package]] 2929 + name = "endi" 2930 + version = "1.1.0" 2931 + source = "registry+https://github.com/rust-lang/crates.io-index" 2932 + checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" 2933 + 2934 + [[package]] 2726 2935 name = "endian-type" 2727 2936 version = "0.1.2" 2728 2937 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2735 2944 checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 2736 2945 dependencies = [ 2737 2946 "heck 0.5.0", 2947 + "proc-macro2", 2948 + "quote", 2949 + "syn 2.0.85", 2950 + ] 2951 + 2952 + [[package]] 2953 + name = "enumflags2" 2954 + version = "0.7.10" 2955 + source = "registry+https://github.com/rust-lang/crates.io-index" 2956 + checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" 2957 + dependencies = [ 2958 + "enumflags2_derive", 2959 + "serde", 2960 + ] 2961 + 2962 + [[package]] 2963 + name = "enumflags2_derive" 2964 + version = "0.7.10" 2965 + source = "registry+https://github.com/rust-lang/crates.io-index" 2966 + checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" 2967 + dependencies = [ 2738 2968 "proc-macro2", 2739 2969 "quote", 2740 2970 "syn 2.0.85", ··· 2802 3032 2803 3033 [[package]] 2804 3034 name = "event-listener" 3035 + version = "2.5.3" 3036 + source = "registry+https://github.com/rust-lang/crates.io-index" 3037 + checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 3038 + 3039 + [[package]] 3040 + name = "event-listener" 2805 3041 version = "5.3.1" 2806 3042 source = "registry+https://github.com/rust-lang/crates.io-index" 2807 3043 checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" ··· 2817 3053 source = "registry+https://github.com/rust-lang/crates.io-index" 2818 3054 checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" 2819 3055 dependencies = [ 2820 - "event-listener", 3056 + "event-listener 5.3.1", 2821 3057 "pin-project-lite", 2822 3058 ] 2823 3059 ··· 3160 3396 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 3161 3397 3162 3398 [[package]] 3399 + name = "futures-lite" 3400 + version = "2.4.0" 3401 + source = "registry+https://github.com/rust-lang/crates.io-index" 3402 + checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" 3403 + dependencies = [ 3404 + "fastrand", 3405 + "futures-core", 3406 + "futures-io", 3407 + "parking", 3408 + "pin-project-lite", 3409 + ] 3410 + 3411 + [[package]] 3163 3412 name = "futures-macro" 3164 3413 version = "0.3.31" 3165 3414 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3264 3513 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 3265 3514 3266 3515 [[package]] 3516 + name = "gloo-timers" 3517 + version = "0.3.0" 3518 + source = "registry+https://github.com/rust-lang/crates.io-index" 3519 + checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" 3520 + dependencies = [ 3521 + "futures-channel", 3522 + "futures-core", 3523 + "js-sys", 3524 + "wasm-bindgen", 3525 + ] 3526 + 3527 + [[package]] 3267 3528 name = "glow" 3268 3529 version = "0.13.1" 3269 3530 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3458 3719 version = "0.3.9" 3459 3720 source = "registry+https://github.com/rust-lang/crates.io-index" 3460 3721 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 3722 + 3723 + [[package]] 3724 + name = "hermit-abi" 3725 + version = "0.4.0" 3726 + source = "registry+https://github.com/rust-lang/crates.io-index" 3727 + checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 3461 3728 3462 3729 [[package]] 3463 3730 name = "hex" ··· 4020 4287 ] 4021 4288 4022 4289 [[package]] 4290 + name = "kv-log-macro" 4291 + version = "1.0.7" 4292 + source = "registry+https://github.com/rust-lang/crates.io-index" 4293 + checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 4294 + dependencies = [ 4295 + "log", 4296 + ] 4297 + 4298 + [[package]] 4023 4299 name = "language-tags" 4024 4300 version = "0.3.2" 4025 4301 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4303 4579 version = "0.4.22" 4304 4580 source = "registry+https://github.com/rust-lang/crates.io-index" 4305 4581 checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 4582 + dependencies = [ 4583 + "value-bag", 4584 + ] 4306 4585 4307 4586 [[package]] 4308 4587 name = "lru" ··· 4512 4791 source = "registry+https://github.com/rust-lang/crates.io-index" 4513 4792 checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 4514 4793 dependencies = [ 4515 - "hermit-abi", 4794 + "hermit-abi 0.3.9", 4516 4795 "libc", 4517 4796 "log", 4518 4797 "wasi", ··· 4526 4805 checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" 4527 4806 4528 4807 [[package]] 4808 + name = "mpris-server" 4809 + version = "0.8.1" 4810 + source = "registry+https://github.com/rust-lang/crates.io-index" 4811 + checksum = "058bc2227727af394f34aa51da3e36aeecf2c808f39315d35f754872660750ae" 4812 + dependencies = [ 4813 + "async-channel 2.3.1", 4814 + "futures-channel", 4815 + "serde", 4816 + "trait-variant", 4817 + "zbus", 4818 + ] 4819 + 4820 + [[package]] 4529 4821 name = "multer" 4530 4822 version = "3.1.0" 4531 4823 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4639 4931 "bitflags 2.6.0", 4640 4932 "cfg-if", 4641 4933 "libc", 4934 + ] 4935 + 4936 + [[package]] 4937 + name = "nix" 4938 + version = "0.29.0" 4939 + source = "registry+https://github.com/rust-lang/crates.io-index" 4940 + checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 4941 + dependencies = [ 4942 + "bitflags 2.6.0", 4943 + "cfg-if", 4944 + "cfg_aliases 0.2.1", 4945 + "libc", 4946 + "memoffset 0.9.1", 4642 4947 ] 4643 4948 4644 4949 [[package]] ··· 4825 5130 source = "registry+https://github.com/rust-lang/crates.io-index" 4826 5131 checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 4827 5132 dependencies = [ 4828 - "hermit-abi", 5133 + "hermit-abi 0.3.9", 4829 5134 "libc", 4830 5135 ] 4831 5136 ··· 4908 5213 checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 4909 5214 dependencies = [ 4910 5215 "num-traits", 5216 + ] 5217 + 5218 + [[package]] 5219 + name = "ordered-stream" 5220 + version = "0.2.0" 5221 + source = "registry+https://github.com/rust-lang/crates.io-index" 5222 + checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 5223 + dependencies = [ 5224 + "futures-core", 5225 + "pin-project-lite", 4911 5226 ] 4912 5227 4913 5228 [[package]] ··· 5216 5531 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 5217 5532 5218 5533 [[package]] 5534 + name = "piper" 5535 + version = "0.2.4" 5536 + source = "registry+https://github.com/rust-lang/crates.io-index" 5537 + checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 5538 + dependencies = [ 5539 + "atomic-waker", 5540 + "fastrand", 5541 + "futures-io", 5542 + ] 5543 + 5544 + [[package]] 5219 5545 name = "pkcs1" 5220 5546 version = "0.7.5" 5221 5547 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5270 5596 "fdeflate", 5271 5597 "flate2", 5272 5598 "miniz_oxide 0.7.4", 5599 + ] 5600 + 5601 + [[package]] 5602 + name = "polling" 5603 + version = "3.7.3" 5604 + source = "registry+https://github.com/rust-lang/crates.io-index" 5605 + checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" 5606 + dependencies = [ 5607 + "cfg-if", 5608 + "concurrent-queue", 5609 + "hermit-abi 0.4.0", 5610 + "pin-project-lite", 5611 + "rustix", 5612 + "tracing", 5613 + "windows-sys 0.59.0", 5273 5614 ] 5274 5615 5275 5616 [[package]] ··· 5954 6295 ] 5955 6296 5956 6297 [[package]] 6298 + name = "rockbox-mpris" 6299 + version = "0.1.0" 6300 + dependencies = [ 6301 + "anyhow", 6302 + "async-std", 6303 + "mpris-server", 6304 + "rockbox-graphql", 6305 + "rockbox-rpc", 6306 + "tokio", 6307 + "urlencoding", 6308 + ] 6309 + 6310 + [[package]] 5957 6311 name = "rockbox-rpc" 5958 6312 version = "0.1.0" 5959 6313 dependencies = [ ··· 5994 6348 version = "0.1.0" 5995 6349 dependencies = [ 5996 6350 "anyhow", 6351 + "async-std", 5997 6352 "md5", 5998 6353 "owo-colors 4.1.0", 5999 6354 "queryst", ··· 6001 6356 "reqwest", 6002 6357 "rockbox-graphql", 6003 6358 "rockbox-library", 6359 + "rockbox-mpris", 6004 6360 "rockbox-rpc", 6005 6361 "rockbox-search", 6006 6362 "rockbox-settings", ··· 6011 6367 "sqlx", 6012 6368 "threadpool", 6013 6369 "tokio", 6370 + "urlencoding", 6014 6371 ] 6015 6372 6016 6373 [[package]] ··· 6478 6835 ] 6479 6836 6480 6837 [[package]] 6838 + name = "serde_repr" 6839 + version = "0.1.19" 6840 + source = "registry+https://github.com/rust-lang/crates.io-index" 6841 + checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" 6842 + dependencies = [ 6843 + "proc-macro2", 6844 + "quote", 6845 + "syn 2.0.85", 6846 + ] 6847 + 6848 + [[package]] 6481 6849 name = "serde_spanned" 6482 6850 version = "0.6.8" 6483 6851 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6780 7148 "crc", 6781 7149 "crossbeam-queue", 6782 7150 "either", 6783 - "event-listener", 7151 + "event-listener 5.3.1", 6784 7152 "futures-channel", 6785 7153 "futures-core", 6786 7154 "futures-intrusive", ··· 8032 8400 ] 8033 8401 8034 8402 [[package]] 8403 + name = "trait-variant" 8404 + version = "0.1.2" 8405 + source = "registry+https://github.com/rust-lang/crates.io-index" 8406 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 8407 + dependencies = [ 8408 + "proc-macro2", 8409 + "quote", 8410 + "syn 2.0.85", 8411 + ] 8412 + 8413 + [[package]] 8035 8414 name = "triomphe" 8036 8415 version = "0.1.13" 8037 8416 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 8125 8504 checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" 8126 8505 8127 8506 [[package]] 8507 + name = "uds_windows" 8508 + version = "1.1.0" 8509 + source = "registry+https://github.com/rust-lang/crates.io-index" 8510 + checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" 8511 + dependencies = [ 8512 + "memoffset 0.9.1", 8513 + "tempfile", 8514 + "winapi", 8515 + ] 8516 + 8517 + [[package]] 8128 8518 name = "unic-char-property" 8129 8519 version = "0.9.0" 8130 8520 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 8266 8656 ] 8267 8657 8268 8658 [[package]] 8659 + name = "urlencoding" 8660 + version = "2.1.3" 8661 + source = "registry+https://github.com/rust-lang/crates.io-index" 8662 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 8663 + 8664 + [[package]] 8269 8665 name = "urlpattern" 8270 8666 version = "0.3.0" 8271 8667 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 8344 8740 checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" 8345 8741 8346 8742 [[package]] 8743 + name = "value-bag" 8744 + version = "1.10.0" 8745 + source = "registry+https://github.com/rust-lang/crates.io-index" 8746 + checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" 8747 + 8748 + [[package]] 8347 8749 name = "value-trait" 8348 8750 version = "0.8.1" 8349 8751 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 8499 8901 "arrayvec", 8500 8902 "bit-vec", 8501 8903 "bitflags 2.6.0", 8502 - "cfg_aliases", 8904 + "cfg_aliases 0.1.1", 8503 8905 "codespan-reporting", 8504 8906 "document-features", 8505 8907 "indexmap 2.5.0", ··· 8531 8933 "bit-set", 8532 8934 "bitflags 2.6.0", 8533 8935 "block", 8534 - "cfg_aliases", 8936 + "cfg_aliases 0.1.1", 8535 8937 "core-graphics-types", 8536 8938 "d3d12", 8537 8939 "glow", ··· 8909 9311 "rusticata-macros", 8910 9312 "thiserror", 8911 9313 "time", 9314 + ] 9315 + 9316 + [[package]] 9317 + name = "xdg-home" 9318 + version = "1.3.0" 9319 + source = "registry+https://github.com/rust-lang/crates.io-index" 9320 + checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" 9321 + dependencies = [ 9322 + "libc", 9323 + "windows-sys 0.59.0", 8912 9324 ] 8913 9325 8914 9326 [[package]] ··· 8942 9354 ] 8943 9355 8944 9356 [[package]] 9357 + name = "zbus" 9358 + version = "4.4.0" 9359 + source = "registry+https://github.com/rust-lang/crates.io-index" 9360 + checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" 9361 + dependencies = [ 9362 + "async-broadcast", 9363 + "async-executor", 9364 + "async-fs", 9365 + "async-io", 9366 + "async-lock", 9367 + "async-process", 9368 + "async-recursion", 9369 + "async-task", 9370 + "async-trait", 9371 + "blocking", 9372 + "enumflags2", 9373 + "event-listener 5.3.1", 9374 + "futures-core", 9375 + "futures-sink", 9376 + "futures-util", 9377 + "hex", 9378 + "nix 0.29.0", 9379 + "ordered-stream", 9380 + "rand", 9381 + "serde", 9382 + "serde_repr", 9383 + "sha1", 9384 + "static_assertions", 9385 + "tracing", 9386 + "uds_windows", 9387 + "windows-sys 0.52.0", 9388 + "xdg-home", 9389 + "zbus_macros", 9390 + "zbus_names", 9391 + "zvariant", 9392 + ] 9393 + 9394 + [[package]] 9395 + name = "zbus_macros" 9396 + version = "4.4.0" 9397 + source = "registry+https://github.com/rust-lang/crates.io-index" 9398 + checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" 9399 + dependencies = [ 9400 + "proc-macro-crate", 9401 + "proc-macro2", 9402 + "quote", 9403 + "syn 2.0.85", 9404 + "zvariant_utils", 9405 + ] 9406 + 9407 + [[package]] 9408 + name = "zbus_names" 9409 + version = "3.0.0" 9410 + source = "registry+https://github.com/rust-lang/crates.io-index" 9411 + checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" 9412 + dependencies = [ 9413 + "serde", 9414 + "static_assertions", 9415 + "zvariant", 9416 + ] 9417 + 9418 + [[package]] 8945 9419 name = "zerocopy" 8946 9420 version = "0.7.35" 8947 9421 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 9030 9504 "cc", 9031 9505 "pkg-config", 9032 9506 ] 9507 + 9508 + [[package]] 9509 + name = "zvariant" 9510 + version = "4.2.0" 9511 + source = "registry+https://github.com/rust-lang/crates.io-index" 9512 + checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" 9513 + dependencies = [ 9514 + "endi", 9515 + "enumflags2", 9516 + "serde", 9517 + "static_assertions", 9518 + "zvariant_derive", 9519 + ] 9520 + 9521 + [[package]] 9522 + name = "zvariant_derive" 9523 + version = "4.2.0" 9524 + source = "registry+https://github.com/rust-lang/crates.io-index" 9525 + checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" 9526 + dependencies = [ 9527 + "proc-macro-crate", 9528 + "proc-macro2", 9529 + "quote", 9530 + "syn 2.0.85", 9531 + "zvariant_utils", 9532 + ] 9533 + 9534 + [[package]] 9535 + name = "zvariant_utils" 9536 + version = "2.1.0" 9537 + source = "registry+https://github.com/rust-lang/crates.io-index" 9538 + checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" 9539 + dependencies = [ 9540 + "proc-macro2", 9541 + "quote", 9542 + "syn 2.0.85", 9543 + ]
+2 -1
README.md
··· 81 81 - [ ] Stream from Spotify 82 82 - [ ] Stream from Tidal 83 83 - [ ] Stream to Chromecast 84 + - [ ] Stream to Kodi 84 85 - [ ] TuneIn Radio 85 86 - [ ] MPD Server 86 - - [ ] MPRIS 87 + - [x] MPRIS 87 88 - [ ] Upnp Player 88 89 - [ ] Airplay 89 90 - [ ] TypeScript ([Deno](https://deno.com)) API (for writing plugins)
+1 -1
apps/settings.c
··· 619 619 switch (options) 620 620 { 621 621 case SETTINGS_SAVE_CHANGED: 622 - if (!is_changed(setting)) 622 + // if (!is_changed(setting)) 623 623 continue; 624 624 break; 625 625 case SETTINGS_SAVE_SOUND:
+2 -4
crates/graphql/src/schema/playback.rs
··· 292 292 293 293 async fn play_track(&self, ctx: &Context<'_>, path: String) -> Result<i32, Error> { 294 294 let client = ctx.data::<reqwest::Client>().unwrap(); 295 - 296 - if !std::path::Path::new(&path).is_file() { 297 - return Err(Error::new("Invalid path")); 298 - } 295 + let path = path.replace("file://", ""); 299 296 300 297 let body = serde_json::json!({ 301 298 "tracks": vec![path], ··· 304 301 let url = format!("{}/playlists", rockbox_url()); 305 302 client.post(&url).json(&body).send().await?; 306 303 304 + let client = reqwest::Client::new(); 307 305 let url = format!("{}/playlists/start", rockbox_url()); 308 306 client.put(&url).send().await?; 309 307
+13
crates/mpris/Cargo.toml
··· 1 + [package] 2 + edition = "2021" 3 + name = "rockbox-mpris" 4 + version = "0.1.0" 5 + 6 + [dependencies] 7 + anyhow = "1.0.93" 8 + async-std = {version = "1.13.0", features = ["unstable", "attributes"]} 9 + mpris-server = "0.8.1" 10 + rockbox-graphql = {path = "../graphql"} 11 + rockbox-rpc = {path = "../rpc"} 12 + tokio = {version = "1.36.0", features = ["full"]} 13 + urlencoding = "2.1.3"
+158
crates/mpris/src/lib.rs
··· 1 + use std::{env, future, sync::Arc}; 2 + 3 + use anyhow::Error; 4 + use async_std::stream::StreamExt; 5 + use mpris_server::{LoopStatus, Metadata, PlaybackStatus, Player, Time, TrackId}; 6 + use rockbox_graphql::{ 7 + schema::objects::{audio_status::AudioStatus, track::Track}, 8 + simplebroker::SimpleBroker, 9 + }; 10 + use rockbox_rpc::api::rockbox::v1alpha1::{ 11 + playback_service_client::PlaybackServiceClient, settings_service_client::SettingsServiceClient, 12 + sound_service_client::SoundServiceClient, AdjustVolumeRequest, GetGlobalSettingsRequest, 13 + HardStopRequest, NextRequest, PauseRequest, PlayRequest, PlayTrackRequest, PreviousRequest, 14 + ResumeRequest, SaveSettingsRequest, 15 + }; 16 + use tokio::sync::Mutex; 17 + 18 + pub mod macros; 19 + 20 + const PLAYER_NAME: &str = "rockbox"; 21 + 22 + pub struct MprisServer { 23 + player: Arc<Player>, 24 + } 25 + 26 + impl MprisServer { 27 + pub async fn start() -> Result<Self, Error> { 28 + let host = env::var("ROCKBOX_HOST").unwrap_or_else(|_| "localhost".to_string()); 29 + let port = env::var("ROCKBOX_PORT").unwrap_or_else(|_| "6061".to_string()); 30 + let url = format!("tcp://{}:{}", host, port); 31 + 32 + let rt = tokio::runtime::Runtime::new()?; 33 + let client = Arc::new(Mutex::new( 34 + rt.block_on(PlaybackServiceClient::connect(url.clone()))?, 35 + )); 36 + let settings_service_client = Arc::new(Mutex::new( 37 + rt.block_on(SettingsServiceClient::connect(url.clone()))?, 38 + )); 39 + let sound_service_client = Arc::new(Mutex::new( 40 + rt.block_on(SoundServiceClient::connect(url.clone()))?, 41 + )); 42 + 43 + let player = Player::builder(PLAYER_NAME) 44 + .can_play(true) 45 + .can_pause(true) 46 + .can_seek(true) 47 + .can_go_next(true) 48 + .can_go_previous(true) 49 + .can_control(true) 50 + .build() 51 + .await?; 52 + 53 + connect_player_action!( 54 + player, 55 + client, 56 + connect_previous, 57 + previous, 58 + PreviousRequest {} 59 + ); 60 + connect_player_action!(player, client, connect_next, next, NextRequest {}); 61 + connect_player_action!(player, client, connect_play, resume, ResumeRequest {}); 62 + connect_player_action!(player, client, connect_pause, pause, PauseRequest {}); 63 + connect_player_seek_action!(player, client); 64 + connect_player_set_position_action!(player, client); 65 + connect_player_action!(player, client, connect_stop, hard_stop, HardStopRequest {}); 66 + connect_player_volume_action!(player, sound_service_client, settings_service_client); 67 + connect_player_shuffle_action!(player, settings_service_client); 68 + connect_player_loop_status_action!(player, settings_service_client); 69 + connect_player_open_uri_action!(player, client); 70 + 71 + let server = MprisServer { 72 + player: Arc::new(player), 73 + }; 74 + 75 + async_std::task::spawn_local(server.player.run()); 76 + 77 + let player_mutex = Arc::new(std::sync::Mutex::new(server.player.clone())); 78 + let player_mutex_clone = Arc::clone(&player_mutex); 79 + 80 + async_std::task::spawn_local(async move { 81 + let mut subscription = SimpleBroker::<AudioStatus>::subscribe(); 82 + while let Some(response) = subscription.next().await { 83 + let player = player_mutex_clone.lock().unwrap(); 84 + match response.status { 85 + 1 => match player.set_playback_status(PlaybackStatus::Playing).await { 86 + Ok(_) => {} 87 + Err(e) => { 88 + eprintln!("Error: {}", e); 89 + } 90 + }, 91 + 3 => match player.set_playback_status(PlaybackStatus::Paused).await { 92 + Ok(_) => {} 93 + Err(e) => { 94 + eprintln!("Error: {}", e); 95 + } 96 + }, 97 + _ => match player.set_playback_status(PlaybackStatus::Stopped).await { 98 + Ok(_) => {} 99 + Err(e) => { 100 + eprintln!("Error: {}", e); 101 + } 102 + }, 103 + } 104 + } 105 + }); 106 + 107 + async_std::task::spawn_local(async move { 108 + let port = std::env::var("ROCKBOX_GRAPHQL_PORT").unwrap_or("6062".to_string()); 109 + let mut subscription = SimpleBroker::<Track>::subscribe(); 110 + while let Some(track) = subscription.next().await { 111 + let player = player_mutex.lock().unwrap(); 112 + let mut metadata = Metadata::builder() 113 + .title(track.title) 114 + .artist([track.artist]) 115 + .album(track.album) 116 + .album_artist([track.album_artist]) 117 + .track_number(track.tracknum) 118 + .disc_number(track.discnum) 119 + .length(Time::from_millis(track.length as i64)); 120 + 121 + if let Some(album_art) = track.album_art { 122 + metadata = match album_art.starts_with("http") { 123 + true => metadata.art_url(album_art), 124 + false => metadata 125 + .art_url(format!("http://localhost:{}/covers/{}", port, album_art)), 126 + } 127 + } 128 + 129 + if let Some(trackid) = track.id { 130 + metadata = metadata.trackid( 131 + TrackId::try_from(format!("/rockbox/tracks/{}", trackid)).unwrap(), 132 + ); 133 + } 134 + 135 + let metadata = metadata.build(); 136 + 137 + match player.set_metadata(metadata).await { 138 + Ok(_) => {} 139 + Err(e) => { 140 + eprintln!("Error: {}", e); 141 + } 142 + } 143 + 144 + player.set_position(Time::from_millis(track.elapsed as i64)); 145 + match player.seeked(Time::from_millis(track.elapsed as i64)).await { 146 + Ok(_) => {} 147 + Err(e) => { 148 + eprintln!("Error: {}", e); 149 + } 150 + } 151 + } 152 + }); 153 + 154 + future::pending::<()>().await; 155 + 156 + Ok(server) 157 + } 158 + }
+197
crates/mpris/src/macros.rs
··· 1 + #[macro_export] 2 + macro_rules! connect_player_action { 3 + ($player:expr, $client:expr, $connect:ident, $action:ident, $request:expr) => {{ 4 + let client = Arc::clone(&$client); 5 + let rt = tokio::runtime::Runtime::new()?; 6 + $player.$connect(move |_player| { 7 + let client = Arc::clone(&client); 8 + match rt.block_on(async move { 9 + let mut client = client.lock().await; 10 + client.$action($request).await?; 11 + Ok::<(), Error>(()) 12 + }) { 13 + Ok(_) => {} 14 + Err(e) => { 15 + eprintln!("Error: {}", e); 16 + } 17 + } 18 + }); 19 + }}; 20 + } 21 + 22 + #[macro_export] 23 + macro_rules! connect_player_seek_action { 24 + ($player:expr, $client:expr) => {{ 25 + let client = Arc::clone(&$client); 26 + let rt = tokio::runtime::Runtime::new()?; 27 + $player.connect_seek(move |_player, time| { 28 + let client = Arc::clone(&client); 29 + match rt.block_on(async move { 30 + let mut client = client.lock().await; 31 + client 32 + .play(PlayRequest { 33 + elapsed: time.as_millis(), 34 + offset: 0, 35 + }) 36 + .await?; 37 + Ok::<(), Error>(()) 38 + }) { 39 + Ok(_) => {} 40 + Err(e) => { 41 + eprintln!("Error: {}", e); 42 + } 43 + } 44 + }); 45 + }}; 46 + } 47 + 48 + #[macro_export] 49 + macro_rules! connect_player_set_position_action { 50 + ($player:expr, $client:expr) => {{ 51 + let client = Arc::clone(&$client); 52 + let rt = tokio::runtime::Runtime::new()?; 53 + $player.connect_set_position(move |_player, _track_id, time| { 54 + let client = Arc::clone(&client); 55 + match rt.block_on(async move { 56 + let mut client = client.lock().await; 57 + client 58 + .play(PlayRequest { 59 + elapsed: time.as_millis(), 60 + offset: 0, 61 + }) 62 + .await?; 63 + Ok::<(), Error>(()) 64 + }) { 65 + Ok(_) => {} 66 + Err(e) => { 67 + eprintln!("Error: {}", e); 68 + } 69 + } 70 + }); 71 + }}; 72 + } 73 + 74 + #[macro_export] 75 + macro_rules! connect_player_open_uri_action { 76 + ($player:expr, $client:expr) => {{ 77 + let client = Arc::clone(&$client); 78 + let rt = tokio::runtime::Runtime::new()?; 79 + $player.connect_open_uri(move |_player, uri| { 80 + let client = Arc::clone(&client); 81 + match rt.block_on(async move { 82 + let mut client = client.lock().await; 83 + let path = uri.to_string(); 84 + let path = match path.starts_with("file://") { 85 + true => urlencoding::decode(&path.replace("file://", "")) 86 + .unwrap() 87 + .to_string(), 88 + false => path, 89 + }; 90 + client.play_track(PlayTrackRequest { path }).await?; 91 + Ok::<(), Error>(()) 92 + }) { 93 + Ok(_) => {} 94 + Err(e) => { 95 + eprintln!("Error: {}", e); 96 + } 97 + } 98 + }); 99 + }}; 100 + } 101 + 102 + #[macro_export] 103 + macro_rules! connect_player_volume_action { 104 + ($player:expr, $client:expr, $settings_client:expr) => { 105 + let client = Arc::clone(&$client); 106 + let settings_client = Arc::clone(&$settings_client); 107 + let rt = tokio::runtime::Runtime::new()?; 108 + $player.connect_set_volume(move |_player, new_volume| { 109 + let client = Arc::clone(&client); 110 + let settings_client = Arc::clone(&settings_client); 111 + let mut settings_client = rt.block_on(settings_client.lock()); 112 + let volume = match rt 113 + .block_on(settings_client.get_global_settings(GetGlobalSettingsRequest {})) 114 + { 115 + Ok(response) => response.into_inner().volume, 116 + Err(e) => { 117 + eprintln!("Error: {}", e); 118 + 0 119 + } 120 + }; 121 + match rt.block_on(async move { 122 + let mut client = client.lock().await; 123 + // new_volume is a float between 0.0 and 1.0 124 + // we need to convert it to an i32 between -80 db and 0 db 125 + // 0.0 -> -80 db 126 + // 1.0 -> 0 db 127 + // volume = -80 + 80 * new_volume 128 + let new_volume = (-80.0 + 80.0 * new_volume) as i32; 129 + let steps = (new_volume as i32 - volume) as i32; 130 + client.adjust_volume(AdjustVolumeRequest { steps }).await?; 131 + Ok::<(), Error>(()) 132 + }) { 133 + Ok(_) => {} 134 + Err(e) => { 135 + eprintln!("Error: {}", e); 136 + } 137 + } 138 + }); 139 + }; 140 + } 141 + 142 + #[macro_export] 143 + macro_rules! connect_player_shuffle_action { 144 + ($player:expr, $client:expr) => { 145 + let client = Arc::clone(&$client); 146 + let rt = tokio::runtime::Runtime::new()?; 147 + $player.connect_set_shuffle(move |_player, new_shuffle| { 148 + let client = Arc::clone(&client); 149 + match rt.block_on(async move { 150 + let mut client = client.lock().await; 151 + client 152 + .save_settings(SaveSettingsRequest { 153 + playlist_shuffle: Some(new_shuffle), 154 + ..Default::default() 155 + }) 156 + .await?; 157 + Ok::<(), Error>(()) 158 + }) { 159 + Ok(_) => {} 160 + Err(e) => { 161 + eprintln!("Error: {}", e); 162 + } 163 + } 164 + }); 165 + }; 166 + } 167 + 168 + #[macro_export] 169 + macro_rules! connect_player_loop_status_action { 170 + ($player:expr, $client:expr) => { 171 + let client = Arc::clone(&$client); 172 + let rt = tokio::runtime::Runtime::new()?; 173 + $player.connect_set_loop_status(move |_player, new_loop_status| { 174 + let client = Arc::clone(&client); 175 + match rt.block_on(async move { 176 + let mut client = client.lock().await; 177 + let repeat_mode = match new_loop_status { 178 + LoopStatus::None => Some(0), 179 + LoopStatus::Playlist => Some(1), 180 + LoopStatus::Track => Some(2), 181 + }; 182 + client 183 + .save_settings(SaveSettingsRequest { 184 + repeat_mode, 185 + ..Default::default() 186 + }) 187 + .await?; 188 + Ok::<(), Error>(()) 189 + }) { 190 + Ok(_) => {} 191 + Err(e) => { 192 + eprintln!("Error: {}", e); 193 + } 194 + } 195 + }); 196 + }; 197 + }
+13 -10
crates/rpc/src/lib.rs
··· 1133 1133 channel_config: self.channel_config, 1134 1134 player_name: self.player_name, 1135 1135 eq_enabled: self.eq_enabled, 1136 - eq_band_settings: Some( 1137 - self.eq_band_settings 1138 - .into_iter() 1139 - .map(|band| EqBandSetting { 1140 - cutoff: band.cutoff, 1141 - q: band.q, 1142 - gain: band.gain, 1143 - }) 1144 - .collect(), 1145 - ), 1136 + eq_band_settings: match self.eq_band_settings.is_empty() { 1137 + true => None, 1138 + false => Some( 1139 + self.eq_band_settings 1140 + .into_iter() 1141 + .map(|band| EqBandSetting { 1142 + cutoff: band.cutoff, 1143 + q: band.q, 1144 + gain: band.gain, 1145 + }) 1146 + .collect(), 1147 + ), 1148 + }, 1146 1149 replaygain_settings: self.replaygain_settings.map(|settings| { 1147 1150 ReplaygainSettings { 1148 1151 noclip: settings.noclip,
+6 -7
crates/rpc/src/playback.rs
··· 1 1 use std::{ 2 2 fs, 3 3 sync::{mpsc::Sender, Arc, Mutex}, 4 + thread, 4 5 }; 5 6 6 7 use crate::{ ··· 386 387 request: tonic::Request<PlayTrackRequest>, 387 388 ) -> Result<tonic::Response<PlayTrackResponse>, tonic::Status> { 388 389 let request = request.into_inner(); 389 - let path = request.path; 390 - 391 - if !std::path::Path::new(&path).is_file() { 392 - return Err(tonic::Status::invalid_argument("Path is not a file")); 393 - } 390 + let path = request.path.replace("file://", ""); 391 + let tracks = vec![path.clone()]; 394 392 395 393 let body = serde_json::json!({ 396 - "tracks": vec![path], 394 + "tracks": tracks, 397 395 }); 398 396 399 397 let url = format!("{}/playlists", rockbox_url()); ··· 404 402 .await 405 403 .map_err(|e| tonic::Status::internal(e.to_string()))?; 406 404 405 + let client = reqwest::Client::new(); 407 406 let url = format!("{}/playlists/start", rockbox_url()); 408 - self.client 407 + client 409 408 .put(&url) 410 409 .send() 411 410 .await
+3
crates/server/Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 anyhow = "1.0.89" 11 + async-std = {version = "1.13.0", features = ["unstable"]} 11 12 md5 = "0.7.0" 12 13 owo-colors = "4.0.0" 13 14 queryst = "3.0.0" ··· 15 16 reqwest = {version = "0.12.7", features = ["blocking", "rustls-tls"], default-features = false} 16 17 rockbox-graphql = {path = "../graphql"} 17 18 rockbox-library = {path = "../library"} 19 + rockbox-mpris = {path = "../mpris"} 18 20 rockbox-rpc = {path = "../rpc"} 19 21 rockbox-search = {path = "../search"} 20 22 rockbox-settings = {path = "../settings"} ··· 25 27 sqlx = {version = "0.8.2", features = ["runtime-tokio", "tls-rustls", "sqlite", "chrono", "derive", "macros"]} 26 28 threadpool = "1.8.1" 27 29 tokio = {version = "1.36.0", features = ["full"]} 30 + urlencoding = "2.1.3"
+13
crates/server/src/lib.rs
··· 6 6 simplebroker::SimpleBroker, 7 7 }; 8 8 use rockbox_library::repo; 9 + use rockbox_mpris::MprisServer; 9 10 use rockbox_sys::events::RockboxCommand; 10 11 use rockbox_sys::{self as rb, types::mp3_entry::Mp3Entry}; 11 12 use std::{ ··· 199 200 } 200 201 } 201 202 }); 203 + 204 + // Wait for the rpc server to start 205 + thread::sleep(std::time::Duration::from_millis(500)); 206 + 207 + thread::spawn( 208 + move || match async_std::task::block_on(MprisServer::start()) { 209 + Ok(_) => {} 210 + Err(e) => { 211 + eprintln!("Error starting mpris server: {}", e); 212 + } 213 + }, 214 + ); 202 215 } 203 216 204 217 #[no_mangle]