Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui

feat: add new terminal user interface

+297 -52
Cargo.lock
··· 57 ] 58 59 [[package]] 60 name = "alsa" 61 version = "0.6.0" 62 source = "registry+https://github.com/rust-lang/crates.io-index" 63 checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" 64 dependencies = [ 65 "alsa-sys", 66 - "bitflags", 67 "libc", 68 "nix", 69 ] ··· 237 dependencies = [ 238 "proc-macro2", 239 "quote", 240 - "syn", 241 ] 242 243 [[package]] ··· 267 dependencies = [ 268 "proc-macro2", 269 "quote", 270 - "syn", 271 ] 272 273 [[package]] ··· 301 dependencies = [ 302 "async-trait", 303 "axum-core", 304 - "bitflags", 305 "bytes", 306 "futures-util", 307 "http", ··· 369 source = "registry+https://github.com/rust-lang/crates.io-index" 370 checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" 371 dependencies = [ 372 - "bitflags", 373 "cexpr", 374 "clang-sys", 375 "lazy_static", ··· 380 "regex", 381 "rustc-hash", 382 "shlex", 383 - "syn", 384 ] 385 386 [[package]] ··· 390 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 391 392 [[package]] 393 name = "block-buffer" 394 version = "0.9.0" 395 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 437 checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 438 439 [[package]] 440 name = "cc" 441 version = "1.0.79" 442 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 493 checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 494 dependencies = [ 495 "atty", 496 - "bitflags", 497 "clap_lex", 498 "indexmap", 499 "strsim", ··· 524 dependencies = [ 525 "bytes", 526 "memchr", 527 ] 528 529 [[package]] ··· 591 source = "registry+https://github.com/rust-lang/crates.io-index" 592 checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" 593 dependencies = [ 594 - "bitflags", 595 "coreaudio-sys", 596 ] 597 ··· 663 ] 664 665 [[package]] 666 name = "crypto-mac" 667 version = "0.10.1" 668 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 679 checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 680 dependencies = [ 681 "quote", 682 - "syn", 683 ] 684 685 [[package]] ··· 704 checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" 705 dependencies = [ 706 "cfg-if", 707 - "hashbrown", 708 "lock_api", 709 "once_cell", 710 "parking_lot_core", ··· 876 dependencies = [ 877 "proc-macro2", 878 "quote", 879 - "syn", 880 ] 881 882 [[package]] ··· 995 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 996 997 [[package]] 998 name = "heck" 999 version = "0.4.1" 1000 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1202 checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 1203 dependencies = [ 1204 "autocfg", 1205 - "hashbrown", 1206 ] 1207 1208 [[package]] 1209 name = "infer" ··· 1236 ] 1237 1238 [[package]] 1239 name = "itoa" 1240 version = "1.0.5" 1241 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1318 checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 1319 dependencies = [ 1320 "arrayvec 0.5.2", 1321 - "bitflags", 1322 "cfg-if", 1323 "ryu", 1324 "static_assertions", ··· 1361 ] 1362 1363 [[package]] 1364 name = "m3u" 1365 version = "1.0.0" 1366 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1489 source = "registry+https://github.com/rust-lang/crates.io-index" 1490 checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" 1491 dependencies = [ 1492 - "bitflags", 1493 "jni-sys", 1494 "ndk-sys 0.3.0", 1495 "num_enum", ··· 1502 source = "registry+https://github.com/rust-lang/crates.io-index" 1503 checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" 1504 dependencies = [ 1505 - "bitflags", 1506 "jni-sys", 1507 "ndk-sys 0.4.1+23.1.7779620", 1508 "num_enum", ··· 1540 source = "registry+https://github.com/rust-lang/crates.io-index" 1541 checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" 1542 dependencies = [ 1543 - "bitflags", 1544 "cc", 1545 "cfg-if", 1546 "libc", ··· 1585 dependencies = [ 1586 "proc-macro2", 1587 "quote", 1588 - "syn", 1589 ] 1590 1591 [[package]] ··· 1625 "proc-macro-crate", 1626 "proc-macro2", 1627 "quote", 1628 - "syn", 1629 ] 1630 1631 [[package]] ··· 1668 1669 [[package]] 1670 name = "once_cell" 1671 - version = "1.17.1" 1672 source = "registry+https://github.com/rust-lang/crates.io-index" 1673 - checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 1674 1675 [[package]] 1676 name = "opaque-debug" ··· 1684 source = "registry+https://github.com/rust-lang/crates.io-index" 1685 checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" 1686 dependencies = [ 1687 - "bitflags", 1688 "cfg-if", 1689 "foreign-types", 1690 "libc", ··· 1701 dependencies = [ 1702 "proc-macro2", 1703 "quote", 1704 - "syn", 1705 ] 1706 1707 [[package]] ··· 1765 ] 1766 1767 [[package]] 1768 name = "peeking_take_while" 1769 version = "0.1.2" 1770 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1809 dependencies = [ 1810 "proc-macro2", 1811 "quote", 1812 - "syn", 1813 ] 1814 1815 [[package]] ··· 1877 checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" 1878 dependencies = [ 1879 "proc-macro2", 1880 - "syn", 1881 ] 1882 1883 [[package]] ··· 1898 1899 [[package]] 1900 name = "proc-macro2" 1901 - version = "1.0.51" 1902 source = "registry+https://github.com/rust-lang/crates.io-index" 1903 - checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 1904 dependencies = [ 1905 "unicode-ident", 1906 ] ··· 1923 dependencies = [ 1924 "bytes", 1925 "heck", 1926 - "itertools", 1927 "lazy_static", 1928 "log", 1929 "multimap", ··· 1932 "prost", 1933 "prost-types", 1934 "regex", 1935 - "syn", 1936 "tempfile", 1937 "which", 1938 ] ··· 1944 checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" 1945 dependencies = [ 1946 "anyhow", 1947 - "itertools", 1948 "proc-macro2", 1949 "quote", 1950 - "syn", 1951 ] 1952 1953 [[package]] ··· 1961 1962 [[package]] 1963 name = "quote" 1964 - version = "1.0.23" 1965 source = "registry+https://github.com/rust-lang/crates.io-index" 1966 - checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 1967 dependencies = [ 1968 "proc-macro2", 1969 ] ··· 2040 ] 2041 2042 [[package]] 2043 name = "raw-window-handle" 2044 version = "0.5.0" 2045 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2054 source = "registry+https://github.com/rust-lang/crates.io-index" 2055 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 2056 dependencies = [ 2057 - "bitflags", 2058 ] 2059 2060 [[package]] ··· 2241 source = "registry+https://github.com/rust-lang/crates.io-index" 2242 checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" 2243 dependencies = [ 2244 - "bitflags", 2245 "core-foundation", 2246 "core-foundation-sys", 2247 "libc", ··· 2275 2276 [[package]] 2277 name = "serde" 2278 - version = "1.0.152" 2279 source = "registry+https://github.com/rust-lang/crates.io-index" 2280 - checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 2281 dependencies = [ 2282 "serde_derive", 2283 ] 2284 2285 [[package]] 2286 name = "serde_derive" 2287 - version = "1.0.152" 2288 source = "registry+https://github.com/rust-lang/crates.io-index" 2289 - checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 2290 dependencies = [ 2291 "proc-macro2", 2292 "quote", 2293 - "syn", 2294 ] 2295 2296 [[package]] ··· 2362 checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" 2363 2364 [[package]] 2365 name = "simple-mutex" 2366 version = "1.1.5" 2367 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2413 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 2414 2415 [[package]] 2416 name = "standback" 2417 version = "0.2.17" 2418 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2451 "quote", 2452 "serde", 2453 "serde_derive", 2454 - "syn", 2455 ] 2456 2457 [[package]] ··· 2467 "serde_derive", 2468 "serde_json", 2469 "sha1", 2470 - "syn", 2471 ] 2472 2473 [[package]] ··· 2483 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2484 2485 [[package]] 2486 name = "subtle" 2487 version = "2.4.1" 2488 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2550 source = "registry+https://github.com/rust-lang/crates.io-index" 2551 checksum = "55a0846e7a2c9a8081ff799fc83a975170417ad2a143f644a77ec2e3e82a2b73" 2552 dependencies = [ 2553 - "bitflags", 2554 "lazy_static", 2555 "log", 2556 "symphonia-core", ··· 2616 checksum = "6b9567e2d8a5f866b2f94f5d366d811e0c6826babcff6d37de9e1a6690d38869" 2617 dependencies = [ 2618 "arrayvec 0.7.2", 2619 - "bitflags", 2620 "bytemuck", 2621 "lazy_static", 2622 "log", ··· 2705 ] 2706 2707 [[package]] 2708 name = "sync_wrapper" 2709 version = "0.1.2" 2710 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2768 dependencies = [ 2769 "proc-macro2", 2770 "quote", 2771 - "syn", 2772 ] 2773 2774 [[package]] ··· 2806 "proc-macro2", 2807 "quote", 2808 "standback", 2809 - "syn", 2810 ] 2811 2812 [[package]] ··· 2860 dependencies = [ 2861 "proc-macro2", 2862 "quote", 2863 - "syn", 2864 ] 2865 2866 [[package]] ··· 2957 "proc-macro2", 2958 "prost-build", 2959 "quote", 2960 - "syn", 2961 ] 2962 2963 [[package]] ··· 3004 source = "registry+https://github.com/rust-lang/crates.io-index" 3005 checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" 3006 dependencies = [ 3007 - "bitflags", 3008 "bytes", 3009 "futures-core", 3010 "futures-util", ··· 3050 dependencies = [ 3051 "proc-macro2", 3052 "quote", 3053 - "syn", 3054 ] 3055 3056 [[package]] ··· 3095 3096 [[package]] 3097 name = "tunein-cli" 3098 - version = "0.1.3" 3099 dependencies = [ 3100 "anyhow", 3101 "clap", 3102 "cpal", 3103 "futures", 3104 "futures-util", 3105 "hyper", ··· 3108 "owo-colors", 3109 "pls", 3110 "prost", 3111 "reqwest", 3112 "rodio", 3113 "surf", 3114 "symphonia", 3115 "termion", ··· 3158 ] 3159 3160 [[package]] 3161 name = "universal-hash" 3162 version = "0.4.1" 3163 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3278 "once_cell", 3279 "proc-macro2", 3280 "quote", 3281 - "syn", 3282 "wasm-bindgen-shared", 3283 ] 3284 ··· 3312 dependencies = [ 3313 "proc-macro2", 3314 "quote", 3315 - "syn", 3316 "wasm-bindgen-backend", 3317 "wasm-bindgen-shared", 3318 ] ··· 3535 dependencies = [ 3536 "winapi", 3537 ]
··· 57 ] 58 59 [[package]] 60 + name = "ahash" 61 + version = "0.8.10" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" 64 + dependencies = [ 65 + "cfg-if", 66 + "once_cell", 67 + "version_check", 68 + "zerocopy", 69 + ] 70 + 71 + [[package]] 72 + name = "allocator-api2" 73 + version = "0.2.16" 74 + source = "registry+https://github.com/rust-lang/crates.io-index" 75 + checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 76 + 77 + [[package]] 78 name = "alsa" 79 version = "0.6.0" 80 source = "registry+https://github.com/rust-lang/crates.io-index" 81 checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" 82 dependencies = [ 83 "alsa-sys", 84 + "bitflags 1.3.2", 85 "libc", 86 "nix", 87 ] ··· 255 dependencies = [ 256 "proc-macro2", 257 "quote", 258 + "syn 1.0.107", 259 ] 260 261 [[package]] ··· 285 dependencies = [ 286 "proc-macro2", 287 "quote", 288 + "syn 1.0.107", 289 ] 290 291 [[package]] ··· 319 dependencies = [ 320 "async-trait", 321 "axum-core", 322 + "bitflags 1.3.2", 323 "bytes", 324 "futures-util", 325 "http", ··· 387 source = "registry+https://github.com/rust-lang/crates.io-index" 388 checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" 389 dependencies = [ 390 + "bitflags 1.3.2", 391 "cexpr", 392 "clang-sys", 393 "lazy_static", ··· 398 "regex", 399 "rustc-hash", 400 "shlex", 401 + "syn 1.0.107", 402 ] 403 404 [[package]] ··· 408 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 409 410 [[package]] 411 + name = "bitflags" 412 + version = "2.4.2" 413 + source = "registry+https://github.com/rust-lang/crates.io-index" 414 + checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 415 + 416 + [[package]] 417 name = "block-buffer" 418 version = "0.9.0" 419 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 461 checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 462 463 [[package]] 464 + name = "cassowary" 465 + version = "0.3.0" 466 + source = "registry+https://github.com/rust-lang/crates.io-index" 467 + checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 468 + 469 + [[package]] 470 + name = "castaway" 471 + version = "0.2.2" 472 + source = "registry+https://github.com/rust-lang/crates.io-index" 473 + checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" 474 + dependencies = [ 475 + "rustversion", 476 + ] 477 + 478 + [[package]] 479 name = "cc" 480 version = "1.0.79" 481 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 532 checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 533 dependencies = [ 534 "atty", 535 + "bitflags 1.3.2", 536 "clap_lex", 537 "indexmap", 538 "strsim", ··· 563 dependencies = [ 564 "bytes", 565 "memchr", 566 + ] 567 + 568 + [[package]] 569 + name = "compact_str" 570 + version = "0.7.1" 571 + source = "registry+https://github.com/rust-lang/crates.io-index" 572 + checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" 573 + dependencies = [ 574 + "castaway", 575 + "cfg-if", 576 + "itoa", 577 + "ryu", 578 + "static_assertions", 579 ] 580 581 [[package]] ··· 643 source = "registry+https://github.com/rust-lang/crates.io-index" 644 checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" 645 dependencies = [ 646 + "bitflags 1.3.2", 647 "coreaudio-sys", 648 ] 649 ··· 715 ] 716 717 [[package]] 718 + name = "crossterm" 719 + version = "0.27.0" 720 + source = "registry+https://github.com/rust-lang/crates.io-index" 721 + checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 722 + dependencies = [ 723 + "bitflags 2.4.2", 724 + "crossterm_winapi", 725 + "libc", 726 + "mio", 727 + "parking_lot", 728 + "signal-hook", 729 + "signal-hook-mio", 730 + "winapi", 731 + ] 732 + 733 + [[package]] 734 + name = "crossterm_winapi" 735 + version = "0.9.1" 736 + source = "registry+https://github.com/rust-lang/crates.io-index" 737 + checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 738 + dependencies = [ 739 + "winapi", 740 + ] 741 + 742 + [[package]] 743 name = "crypto-mac" 744 version = "0.10.1" 745 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 756 checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 757 dependencies = [ 758 "quote", 759 + "syn 1.0.107", 760 ] 761 762 [[package]] ··· 781 checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" 782 dependencies = [ 783 "cfg-if", 784 + "hashbrown 0.12.3", 785 "lock_api", 786 "once_cell", 787 "parking_lot_core", ··· 953 dependencies = [ 954 "proc-macro2", 955 "quote", 956 + "syn 1.0.107", 957 ] 958 959 [[package]] ··· 1072 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1073 1074 [[package]] 1075 + name = "hashbrown" 1076 + version = "0.14.3" 1077 + source = "registry+https://github.com/rust-lang/crates.io-index" 1078 + checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 1079 + dependencies = [ 1080 + "ahash", 1081 + "allocator-api2", 1082 + ] 1083 + 1084 + [[package]] 1085 name = "heck" 1086 version = "0.4.1" 1087 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1289 checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 1290 dependencies = [ 1291 "autocfg", 1292 + "hashbrown 0.12.3", 1293 ] 1294 + 1295 + [[package]] 1296 + name = "indoc" 1297 + version = "2.0.4" 1298 + source = "registry+https://github.com/rust-lang/crates.io-index" 1299 + checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" 1300 1301 [[package]] 1302 name = "infer" ··· 1329 ] 1330 1331 [[package]] 1332 + name = "itertools" 1333 + version = "0.12.1" 1334 + source = "registry+https://github.com/rust-lang/crates.io-index" 1335 + checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1336 + dependencies = [ 1337 + "either", 1338 + ] 1339 + 1340 + [[package]] 1341 name = "itoa" 1342 version = "1.0.5" 1343 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1420 checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 1421 dependencies = [ 1422 "arrayvec 0.5.2", 1423 + "bitflags 1.3.2", 1424 "cfg-if", 1425 "ryu", 1426 "static_assertions", ··· 1463 ] 1464 1465 [[package]] 1466 + name = "lru" 1467 + version = "0.12.3" 1468 + source = "registry+https://github.com/rust-lang/crates.io-index" 1469 + checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" 1470 + dependencies = [ 1471 + "hashbrown 0.14.3", 1472 + ] 1473 + 1474 + [[package]] 1475 name = "m3u" 1476 version = "1.0.0" 1477 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1600 source = "registry+https://github.com/rust-lang/crates.io-index" 1601 checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" 1602 dependencies = [ 1603 + "bitflags 1.3.2", 1604 "jni-sys", 1605 "ndk-sys 0.3.0", 1606 "num_enum", ··· 1613 source = "registry+https://github.com/rust-lang/crates.io-index" 1614 checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" 1615 dependencies = [ 1616 + "bitflags 1.3.2", 1617 "jni-sys", 1618 "ndk-sys 0.4.1+23.1.7779620", 1619 "num_enum", ··· 1651 source = "registry+https://github.com/rust-lang/crates.io-index" 1652 checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" 1653 dependencies = [ 1654 + "bitflags 1.3.2", 1655 "cc", 1656 "cfg-if", 1657 "libc", ··· 1696 dependencies = [ 1697 "proc-macro2", 1698 "quote", 1699 + "syn 1.0.107", 1700 ] 1701 1702 [[package]] ··· 1736 "proc-macro-crate", 1737 "proc-macro2", 1738 "quote", 1739 + "syn 1.0.107", 1740 ] 1741 1742 [[package]] ··· 1779 1780 [[package]] 1781 name = "once_cell" 1782 + version = "1.19.0" 1783 source = "registry+https://github.com/rust-lang/crates.io-index" 1784 + checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1785 1786 [[package]] 1787 name = "opaque-debug" ··· 1795 source = "registry+https://github.com/rust-lang/crates.io-index" 1796 checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" 1797 dependencies = [ 1798 + "bitflags 1.3.2", 1799 "cfg-if", 1800 "foreign-types", 1801 "libc", ··· 1812 dependencies = [ 1813 "proc-macro2", 1814 "quote", 1815 + "syn 1.0.107", 1816 ] 1817 1818 [[package]] ··· 1876 ] 1877 1878 [[package]] 1879 + name = "paste" 1880 + version = "1.0.14" 1881 + source = "registry+https://github.com/rust-lang/crates.io-index" 1882 + checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 1883 + 1884 + [[package]] 1885 name = "peeking_take_while" 1886 version = "0.1.2" 1887 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1926 dependencies = [ 1927 "proc-macro2", 1928 "quote", 1929 + "syn 1.0.107", 1930 ] 1931 1932 [[package]] ··· 1994 checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" 1995 dependencies = [ 1996 "proc-macro2", 1997 + "syn 1.0.107", 1998 ] 1999 2000 [[package]] ··· 2015 2016 [[package]] 2017 name = "proc-macro2" 2018 + version = "1.0.78" 2019 source = "registry+https://github.com/rust-lang/crates.io-index" 2020 + checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 2021 dependencies = [ 2022 "unicode-ident", 2023 ] ··· 2040 dependencies = [ 2041 "bytes", 2042 "heck", 2043 + "itertools 0.10.5", 2044 "lazy_static", 2045 "log", 2046 "multimap", ··· 2049 "prost", 2050 "prost-types", 2051 "regex", 2052 + "syn 1.0.107", 2053 "tempfile", 2054 "which", 2055 ] ··· 2061 checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" 2062 dependencies = [ 2063 "anyhow", 2064 + "itertools 0.10.5", 2065 "proc-macro2", 2066 "quote", 2067 + "syn 1.0.107", 2068 ] 2069 2070 [[package]] ··· 2078 2079 [[package]] 2080 name = "quote" 2081 + version = "1.0.35" 2082 source = "registry+https://github.com/rust-lang/crates.io-index" 2083 + checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 2084 dependencies = [ 2085 "proc-macro2", 2086 ] ··· 2157 ] 2158 2159 [[package]] 2160 + name = "ratatui" 2161 + version = "0.26.1" 2162 + source = "registry+https://github.com/rust-lang/crates.io-index" 2163 + checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" 2164 + dependencies = [ 2165 + "bitflags 2.4.2", 2166 + "cassowary", 2167 + "compact_str", 2168 + "crossterm", 2169 + "indoc", 2170 + "itertools 0.12.1", 2171 + "lru", 2172 + "paste", 2173 + "stability", 2174 + "strum", 2175 + "unicode-segmentation", 2176 + "unicode-width", 2177 + ] 2178 + 2179 + [[package]] 2180 name = "raw-window-handle" 2181 version = "0.5.0" 2182 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2191 source = "registry+https://github.com/rust-lang/crates.io-index" 2192 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 2193 dependencies = [ 2194 + "bitflags 1.3.2", 2195 ] 2196 2197 [[package]] ··· 2378 source = "registry+https://github.com/rust-lang/crates.io-index" 2379 checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" 2380 dependencies = [ 2381 + "bitflags 1.3.2", 2382 "core-foundation", 2383 "core-foundation-sys", 2384 "libc", ··· 2412 2413 [[package]] 2414 name = "serde" 2415 + version = "1.0.197" 2416 source = "registry+https://github.com/rust-lang/crates.io-index" 2417 + checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 2418 dependencies = [ 2419 "serde_derive", 2420 ] 2421 2422 [[package]] 2423 name = "serde_derive" 2424 + version = "1.0.197" 2425 source = "registry+https://github.com/rust-lang/crates.io-index" 2426 + checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 2427 dependencies = [ 2428 "proc-macro2", 2429 "quote", 2430 + "syn 2.0.51", 2431 ] 2432 2433 [[package]] ··· 2499 checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" 2500 2501 [[package]] 2502 + name = "signal-hook" 2503 + version = "0.3.17" 2504 + source = "registry+https://github.com/rust-lang/crates.io-index" 2505 + checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 2506 + dependencies = [ 2507 + "libc", 2508 + "signal-hook-registry", 2509 + ] 2510 + 2511 + [[package]] 2512 + name = "signal-hook-mio" 2513 + version = "0.2.3" 2514 + source = "registry+https://github.com/rust-lang/crates.io-index" 2515 + checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 2516 + dependencies = [ 2517 + "libc", 2518 + "mio", 2519 + "signal-hook", 2520 + ] 2521 + 2522 + [[package]] 2523 + name = "signal-hook-registry" 2524 + version = "1.4.1" 2525 + source = "registry+https://github.com/rust-lang/crates.io-index" 2526 + checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 2527 + dependencies = [ 2528 + "libc", 2529 + ] 2530 + 2531 + [[package]] 2532 name = "simple-mutex" 2533 version = "1.1.5" 2534 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2580 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 2581 2582 [[package]] 2583 + name = "stability" 2584 + version = "0.1.1" 2585 + source = "registry+https://github.com/rust-lang/crates.io-index" 2586 + checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" 2587 + dependencies = [ 2588 + "quote", 2589 + "syn 1.0.107", 2590 + ] 2591 + 2592 + [[package]] 2593 name = "standback" 2594 version = "0.2.17" 2595 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2628 "quote", 2629 "serde", 2630 "serde_derive", 2631 + "syn 1.0.107", 2632 ] 2633 2634 [[package]] ··· 2644 "serde_derive", 2645 "serde_json", 2646 "sha1", 2647 + "syn 1.0.107", 2648 ] 2649 2650 [[package]] ··· 2660 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2661 2662 [[package]] 2663 + name = "strum" 2664 + version = "0.26.1" 2665 + source = "registry+https://github.com/rust-lang/crates.io-index" 2666 + checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" 2667 + dependencies = [ 2668 + "strum_macros", 2669 + ] 2670 + 2671 + [[package]] 2672 + name = "strum_macros" 2673 + version = "0.26.1" 2674 + source = "registry+https://github.com/rust-lang/crates.io-index" 2675 + checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" 2676 + dependencies = [ 2677 + "heck", 2678 + "proc-macro2", 2679 + "quote", 2680 + "rustversion", 2681 + "syn 2.0.51", 2682 + ] 2683 + 2684 + [[package]] 2685 name = "subtle" 2686 version = "2.4.1" 2687 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2749 source = "registry+https://github.com/rust-lang/crates.io-index" 2750 checksum = "55a0846e7a2c9a8081ff799fc83a975170417ad2a143f644a77ec2e3e82a2b73" 2751 dependencies = [ 2752 + "bitflags 1.3.2", 2753 "lazy_static", 2754 "log", 2755 "symphonia-core", ··· 2815 checksum = "6b9567e2d8a5f866b2f94f5d366d811e0c6826babcff6d37de9e1a6690d38869" 2816 dependencies = [ 2817 "arrayvec 0.7.2", 2818 + "bitflags 1.3.2", 2819 "bytemuck", 2820 "lazy_static", 2821 "log", ··· 2904 ] 2905 2906 [[package]] 2907 + name = "syn" 2908 + version = "2.0.51" 2909 + source = "registry+https://github.com/rust-lang/crates.io-index" 2910 + checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" 2911 + dependencies = [ 2912 + "proc-macro2", 2913 + "quote", 2914 + "unicode-ident", 2915 + ] 2916 + 2917 + [[package]] 2918 name = "sync_wrapper" 2919 version = "0.1.2" 2920 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2978 dependencies = [ 2979 "proc-macro2", 2980 "quote", 2981 + "syn 1.0.107", 2982 ] 2983 2984 [[package]] ··· 3016 "proc-macro2", 3017 "quote", 3018 "standback", 3019 + "syn 1.0.107", 3020 ] 3021 3022 [[package]] ··· 3070 dependencies = [ 3071 "proc-macro2", 3072 "quote", 3073 + "syn 1.0.107", 3074 ] 3075 3076 [[package]] ··· 3167 "proc-macro2", 3168 "prost-build", 3169 "quote", 3170 + "syn 1.0.107", 3171 ] 3172 3173 [[package]] ··· 3214 source = "registry+https://github.com/rust-lang/crates.io-index" 3215 checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" 3216 dependencies = [ 3217 + "bitflags 1.3.2", 3218 "bytes", 3219 "futures-core", 3220 "futures-util", ··· 3260 dependencies = [ 3261 "proc-macro2", 3262 "quote", 3263 + "syn 1.0.107", 3264 ] 3265 3266 [[package]] ··· 3305 3306 [[package]] 3307 name = "tunein-cli" 3308 + version = "0.2.0" 3309 dependencies = [ 3310 "anyhow", 3311 "clap", 3312 "cpal", 3313 + "crossterm", 3314 "futures", 3315 "futures-util", 3316 "hyper", ··· 3319 "owo-colors", 3320 "pls", 3321 "prost", 3322 + "ratatui", 3323 "reqwest", 3324 "rodio", 3325 + "serde", 3326 "surf", 3327 "symphonia", 3328 "termion", ··· 3371 ] 3372 3373 [[package]] 3374 + name = "unicode-segmentation" 3375 + version = "1.11.0" 3376 + source = "registry+https://github.com/rust-lang/crates.io-index" 3377 + checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 3378 + 3379 + [[package]] 3380 + name = "unicode-width" 3381 + version = "0.1.11" 3382 + source = "registry+https://github.com/rust-lang/crates.io-index" 3383 + checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 3384 + 3385 + [[package]] 3386 name = "universal-hash" 3387 version = "0.4.1" 3388 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3503 "once_cell", 3504 "proc-macro2", 3505 "quote", 3506 + "syn 1.0.107", 3507 "wasm-bindgen-shared", 3508 ] 3509 ··· 3537 dependencies = [ 3538 "proc-macro2", 3539 "quote", 3540 + "syn 1.0.107", 3541 "wasm-bindgen-backend", 3542 "wasm-bindgen-shared", 3543 ] ··· 3760 dependencies = [ 3761 "winapi", 3762 ] 3763 + 3764 + [[package]] 3765 + name = "zerocopy" 3766 + version = "0.7.32" 3767 + source = "registry+https://github.com/rust-lang/crates.io-index" 3768 + checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 3769 + dependencies = [ 3770 + "zerocopy-derive", 3771 + ] 3772 + 3773 + [[package]] 3774 + name = "zerocopy-derive" 3775 + version = "0.7.32" 3776 + source = "registry+https://github.com/rust-lang/crates.io-index" 3777 + checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 3778 + dependencies = [ 3779 + "proc-macro2", 3780 + "quote", 3781 + "syn 2.0.51", 3782 + ]
+24 -22
Cargo.toml
··· 1 [package] 2 - name = "tunein-cli" 3 - version = "0.1.3" 4 edition = "2021" 5 - repository = "https://github.com/tsirysndr/tunein-cli" 6 license = "MIT" 7 readme = "README.md" 8 - authors = ["Tsiry Sandratraina <tsiry.sndr@aol.com>"] 9 - categories = ["command-line-utilities"] 10 - keywords = ["radio", "api", "tokio", "web", "tunein"] 11 - description = "Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ" 12 13 [[bin]] 14 name = "tunein" ··· 20 anyhow = "1.0.69" 21 clap = "3.2.20" 22 cpal = "0.14.0" 23 - owo-colors = "3.5.0" 24 - tunein = "0.1.2" 25 - tokio = { version = "1.24.2", features = ["tokio-macros", "macros", "rt", "rt-multi-thread"] } 26 - pls = "0.2.2" 27 - m3u = "1.0.0" 28 - symphonia = { version = "0.5.1", features = ["aac", "alac", "mp3", "isomp4", "flac"] } 29 - hyper = { version = "0.14.23", features = ["client", "stream", "tcp", "http1", "http2"] } 30 - surf = { version = "2.3.2", features = ["h1-client-rustls"], default-features = false} 31 futures-util = "0.3.26" 32 - url = "2.3.1" 33 - rodio = { version = "0.16" } 34 - reqwest = { version = "0.11.14", features = ["blocking"] } 35 minimp3 = "0.5.1" 36 tonic = "0.8.3" 37 - prost = "0.11.8" 38 tonic-web = "0.4.0" 39 - futures = "0.3.26" 40 - termion = "2.0.1" 41 - 42 43 [build-dependencies] 44 tonic-build = "0.8"
··· 1 [package] 2 + authors = ["Tsiry Sandratraina <tsiry.sndr@aol.com>"] 3 + categories = ["command-line-utilities"] 4 + description = "Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ" 5 edition = "2021" 6 + keywords = ["radio", "api", "tokio", "web", "tunein"] 7 license = "MIT" 8 + name = "tunein-cli" 9 readme = "README.md" 10 + repository = "https://github.com/tsirysndr/tunein-cli" 11 + version = "0.2.0" 12 13 [[bin]] 14 name = "tunein" ··· 20 anyhow = "1.0.69" 21 clap = "3.2.20" 22 cpal = "0.14.0" 23 + crossterm = "0.27.0" 24 + futures = "0.3.26" 25 futures-util = "0.3.26" 26 + hyper = {version = "0.14.23", features = ["client", "stream", "tcp", "http1", "http2"]} 27 + m3u = "1.0.0" 28 minimp3 = "0.5.1" 29 + owo-colors = "3.5.0" 30 + pls = "0.2.2" 31 + prost = "0.11.8" 32 + ratatui = "0.26.1" 33 + reqwest = {version = "0.11.14", features = ["blocking"]} 34 + rodio = {version = "0.16"} 35 + serde = "1.0.197" 36 + surf = {version = "2.3.2", features = ["h1-client-rustls"], default-features = false} 37 + symphonia = {version = "0.5.1", features = ["aac", "alac", "mp3", "isomp4", "flac"]} 38 + termion = "2.0.1" 39 + tokio = {version = "1.24.2", features = ["tokio-macros", "macros", "rt", "rt-multi-thread"]} 40 tonic = "0.8.3" 41 tonic-web = "0.4.0" 42 + tunein = "0.1.2" 43 + url = "2.3.1" 44 45 [build-dependencies] 46 tonic-build = "0.8"
+1 -1
LICENSE
··· 1 - Copyright (c) 2023 Tsiry Sandratraina <tsiry.sndr@aol.com> 2 3 Permission is hereby granted, free of charge, to any person obtaining a copy 4 of this software and associated documentation files (the "Software"), to deal
··· 1 + Copyright (c) 2023 Tsiry Sandratraina <tsiry.sndr@fluentci.io> 2 3 Permission is hereby granted, free of charge, to any person obtaining a copy 4 of this software and associated documentation files (the "Software"), to deal
+3
README.md
··· 7 <a href="https://crates.io/crates/tunein-cli" target="_blank"> 8 <img src="https://img.shields.io/crates/v/tunein-cli.svg" /> 9 </a> 10 <a href="https://crates.io/crates/tunein-cli" target="_blank"> 11 <img src="https://img.shields.io/crates/dr/tunein-cli" /> 12 </a>
··· 7 <a href="https://crates.io/crates/tunein-cli" target="_blank"> 8 <img src="https://img.shields.io/crates/v/tunein-cli.svg" /> 9 </a> 10 + <a href="https://flakestry.dev/flake/github/tsirysndr/tunein-cli" target="_blank"> 11 + <img src="https://flakestry.dev/api/badge/flake/github/tsirysdnr/tunein-cli" /> 12 + </a> 13 <a href="https://crates.io/crates/tunein-cli" target="_blank"> 14 <img src="https://img.shields.io/crates/dr/tunein-cli" /> 15 </a>
+123
src/app.rs
···
··· 1 + use std::{ 2 + io, 3 + sync::{Arc, Mutex}, 4 + }; 5 + 6 + use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; 7 + use ratatui::{ 8 + prelude::*, 9 + widgets::{block::*, *}, 10 + }; 11 + use tokio::sync::mpsc::UnboundedReceiver; 12 + 13 + use crate::tui; 14 + 15 + #[derive(Debug, Default, Clone)] 16 + pub struct State { 17 + pub name: String, 18 + pub now_playing: String, 19 + pub genre: String, 20 + pub description: String, 21 + pub br: String, 22 + } 23 + 24 + impl State { 25 + pub fn update(&mut self, state: State) { 26 + *self = state; 27 + } 28 + } 29 + 30 + #[derive(Debug, Default)] 31 + pub struct App { 32 + pub state: Arc<Mutex<State>>, 33 + exit: bool, 34 + } 35 + 36 + impl App { 37 + pub fn new() -> Self { 38 + let state = Arc::new(Mutex::new(State::default())); 39 + Self { state, exit: false } 40 + } 41 + } 42 + 43 + impl App { 44 + /// runs the application's main loop until the user quits 45 + pub async fn run( 46 + &mut self, 47 + terminal: &mut tui::Tui, 48 + mut cmd_rx: UnboundedReceiver<State>, 49 + ) -> anyhow::Result<()> { 50 + let new_state = cmd_rx.recv().await.unwrap(); 51 + self.state.lock().unwrap().update(new_state); 52 + while !self.exit { 53 + terminal.draw(|frame| self.render_frame(frame))?; 54 + self.handle_events()?; 55 + } 56 + Ok(()) 57 + } 58 + 59 + fn render_frame(&self, frame: &mut Frame) { 60 + let areas = Layout::new( 61 + Direction::Vertical, 62 + [ 63 + Constraint::Length(1), 64 + Constraint::Length(1), 65 + Constraint::Length(1), 66 + Constraint::Length(1), 67 + Constraint::Length(1), 68 + Constraint::Min(0), 69 + ], 70 + ) 71 + .split(frame.size()); 72 + 73 + frame.render_widget( 74 + Block::new() 75 + .borders(Borders::TOP) 76 + .title(" TuneIn CLI ") 77 + .title_alignment(Alignment::Center), 78 + areas[0], 79 + ); 80 + 81 + let state = self.state.lock().unwrap(); 82 + 83 + self.render_line("Station ", &state.name, areas[1], frame); 84 + self.render_line("Now Playing ", &state.now_playing, areas[2], frame); 85 + self.render_line("Genre ", &state.genre, areas[3], frame); 86 + self.render_line("Description ", &state.description, areas[4], frame); 87 + self.render_line("Bitrate ", &format!("{} kbps", &state.br), areas[5], frame); 88 + } 89 + 90 + fn render_line(&self, label: &str, value: &str, area: Rect, frame: &mut Frame) { 91 + let span1 = Span::styled(label, Style::new().fg(Color::LightBlue)); 92 + let span2 = Span::raw(value); 93 + 94 + let line = Line::from(vec![span1, span2]); 95 + let text: Text = Text::from(vec![line]); 96 + 97 + frame.render_widget(Paragraph::new(text), area); 98 + } 99 + 100 + /// updates the application's state based on user input 101 + fn handle_events(&mut self) -> io::Result<()> { 102 + match event::read()? { 103 + // it's important to check that the event is a key press event as 104 + // crossterm also emits key release and repeat events on Windows. 105 + Event::Key(key_event) if key_event.kind == KeyEventKind::Press => { 106 + self.handle_key_event(key_event) 107 + } 108 + _ => {} 109 + }; 110 + Ok(()) 111 + } 112 + 113 + fn handle_key_event(&mut self, key_event: KeyEvent) { 114 + match key_event.code { 115 + KeyCode::Char('q') => self.exit(), 116 + _ => {} 117 + } 118 + } 119 + 120 + fn exit(&mut self) { 121 + self.exit = true; 122 + } 123 + }
+27
src/extract.rs
··· 1 use std::time::Duration; 2 3 use anyhow::Error; 4 use surf::{Client, Config, Url}; 5 6 pub async fn extract_stream_url(url: &str, playlist_type: Option<String>) -> Result<String, Error> { 7 match playlist_type { ··· 37 None => Ok(url.to_string()), 38 } 39 }
··· 1 use std::time::Duration; 2 3 use anyhow::Error; 4 + use serde::Deserialize; 5 use surf::{Client, Config, Url}; 6 + 7 + #[derive(Deserialize)] 8 + pub struct Header { 9 + #[serde(rename = "Title")] 10 + pub title: String, 11 + #[serde(rename = "Subtitle")] 12 + pub subtitle: String, 13 + } 14 + 15 + #[derive(Deserialize)] 16 + pub struct NowPlaying { 17 + #[serde(rename = "Header")] 18 + pub header: Header, 19 + } 20 21 pub async fn extract_stream_url(url: &str, playlist_type: Option<String>) -> Result<String, Error> { 22 match playlist_type { ··· 52 None => Ok(url.to_string()), 53 } 54 } 55 + 56 + pub async fn get_currently_playing(station: &str) -> Result<String, Error> { 57 + let client = Client::new(); 58 + let url = format!("https://feed.tunein.com/profiles/{}/nowPlaying", station); 59 + let response: NowPlaying = client 60 + .get(Url::parse(&url)?) 61 + .recv_json() 62 + .await 63 + .map_err(|e| Error::msg(e.to_string()))?; 64 + 65 + Ok(response.header.subtitle) 66 + }
+3 -1
src/main.rs
··· 1 use anyhow::Error; 2 use clap::{arg, Command}; 3 4 mod browse; 5 mod decoder; 6 mod extract; ··· 8 mod player; 9 mod search; 10 mod server; 11 12 fn cli() -> Command<'static> { 13 const VESRION: &str = env!("CARGO_PKG_VERSION"); 14 Command::new("tunein") 15 .version(VESRION) 16 - .author("Tsiry Sandratraina <tsiry.sndr@aol.com>") 17 .about( 18 r#" 19 ______ ____ _______ ____
··· 1 use anyhow::Error; 2 use clap::{arg, Command}; 3 4 + mod app; 5 mod browse; 6 mod decoder; 7 mod extract; ··· 9 mod player; 10 mod search; 11 mod server; 12 + mod tui; 13 14 fn cli() -> Command<'static> { 15 const VESRION: &str = env!("CARGO_PKG_VERSION"); 16 Command::new("tunein") 17 .version(VESRION) 18 + .author("Tsiry Sandratraina <tsiry.sndr@fluentci.io>") 19 .about( 20 r#" 21 ______ ____ _______ ____
+47 -26
src/play.rs
··· 1 - use std::time::Duration; 2 3 use anyhow::Error; 4 - use owo_colors::OwoColorize; 5 - use termion::{clear, cursor}; 6 use tunein::TuneInClient; 7 8 - use crate::{decoder::Mp3Decoder, extract::extract_stream_url}; 9 - 10 - const METER_CHAR: char = 'โ–ˆ'; 11 12 pub async fn exec(name_or_id: &str) -> Result<(), Error> { 13 let client = TuneInClient::new(); ··· 15 .get_station(name_or_id) 16 .await 17 .map_err(|e| Error::msg(e.to_string()))?; 18 - let (url, playlist_type, _) = match results.is_empty() { 19 true => { 20 let results = client 21 .search(name_or_id) ··· 36 station.url.clone(), 37 station.playlist_type.clone(), 38 station.media_type.clone(), 39 ) 40 } 41 - None => ("".to_string(), None, "".to_string()), 42 } 43 } 44 false => { ··· 47 result.url.clone(), 48 result.playlist_type.clone(), 49 result.media_type.clone(), 50 ) 51 } 52 }; 53 let stream_url = extract_stream_url(&url, playlist_type).await?; 54 println!("{}", stream_url); 55 56 - tokio::task::spawn_blocking(move || { 57 let client = reqwest::blocking::Client::new(); 58 59 let response = client.get(stream_url).send().unwrap(); 60 61 - println!("headers: {:#?}", response.headers()); 62 let location = response.headers().get("location"); 63 64 let response = match location { ··· 79 sink.append(decoder); 80 81 loop { 82 - let level = sink.volume(); 83 - display_vu_meter(level); 84 std::thread::sleep(Duration::from_millis(10)); 85 } 86 - }) 87 - .await?; 88 - 89 - Ok(()) 90 - } 91 92 - fn display_vu_meter(level: f32) { 93 - print!("{}{}", clear::All, cursor::Goto(1, 1)); 94 - for i in 0..20 { 95 - if (i as f32) / 20.0 <= level { 96 - print!("{}", METER_CHAR.bright_yellow()); 97 - } else { 98 - print!("{}", METER_CHAR.bright_yellow()); 99 - } 100 - } 101 }
··· 1 + use std::{thread, time::Duration}; 2 3 use anyhow::Error; 4 use tunein::TuneInClient; 5 6 + use crate::{ 7 + app::{App, State}, 8 + decoder::Mp3Decoder, 9 + extract::{extract_stream_url, get_currently_playing}, 10 + tui, 11 + }; 12 13 pub async fn exec(name_or_id: &str) -> Result<(), Error> { 14 let client = TuneInClient::new(); ··· 16 .get_station(name_or_id) 17 .await 18 .map_err(|e| Error::msg(e.to_string()))?; 19 + let (url, playlist_type, _, id) = match results.is_empty() { 20 true => { 21 let results = client 22 .search(name_or_id) ··· 37 station.url.clone(), 38 station.playlist_type.clone(), 39 station.media_type.clone(), 40 + id.clone(), 41 ) 42 } 43 + None => ("".to_string(), None, "".to_string(), "".to_string()), 44 } 45 } 46 false => { ··· 49 result.url.clone(), 50 result.playlist_type.clone(), 51 result.media_type.clone(), 52 + name_or_id.to_string(), 53 ) 54 } 55 }; 56 + let now_playing = get_currently_playing(&id).await?; 57 let stream_url = extract_stream_url(&url, playlist_type).await?; 58 println!("{}", stream_url); 59 60 + let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<State>(); 61 + 62 + let mut app = App::new(); 63 + 64 + thread::spawn(move || { 65 let client = reqwest::blocking::Client::new(); 66 67 let response = client.get(stream_url).send().unwrap(); 68 69 + let headers = response.headers(); 70 + cmd_tx 71 + .send(State { 72 + name: headers 73 + .get("icy-name") 74 + .unwrap() 75 + .to_str() 76 + .unwrap() 77 + .to_string(), 78 + now_playing, 79 + genre: headers 80 + .get("icy-genre") 81 + .unwrap() 82 + .to_str() 83 + .unwrap() 84 + .to_string(), 85 + description: headers 86 + .get("icy-description") 87 + .unwrap() 88 + .to_str() 89 + .unwrap() 90 + .to_string(), 91 + br: headers.get("icy-br").unwrap().to_str().unwrap().to_string(), 92 + }) 93 + .unwrap(); 94 let location = response.headers().get("location"); 95 96 let response = match location { ··· 111 sink.append(decoder); 112 113 loop { 114 std::thread::sleep(Duration::from_millis(10)); 115 } 116 + }); 117 118 + let mut terminal = tui::init()?; 119 + let app_result = app.run(&mut terminal, cmd_rx).await; 120 + tui::restore()?; 121 + app_result 122 }
+23
src/tui.rs
···
··· 1 + use std::io::{self, stderr, stdout, Stdout}; 2 + 3 + use crossterm::{execute, terminal::*}; 4 + use ratatui::prelude::*; 5 + 6 + /// A type alias for the terminal type used in this application 7 + pub type Tui = Terminal<CrosstermBackend<Stdout>>; 8 + 9 + /// Initialize the terminal 10 + pub fn init() -> io::Result<Tui> { 11 + execute!(stdout(), EnterAlternateScreen)?; 12 + execute!(stderr(), EnterAlternateScreen)?; 13 + enable_raw_mode()?; 14 + Terminal::new(CrosstermBackend::new(stdout())) 15 + } 16 + 17 + /// Restore the terminal to its original state 18 + pub fn restore() -> io::Result<()> { 19 + execute!(stdout(), LeaveAlternateScreen)?; 20 + execute!(stderr(), LeaveAlternateScreen)?; 21 + disable_raw_mode()?; 22 + Ok(()) 23 + }