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

Merge pull request #21 from tsirysndr/radiobrowser-provider

feat(provider): add new radio provider: radio-browser

authored by tsiry-sandratraina.com and committed by GitHub d0b756ba f56d6cf4

+24
.github/workflows/ci.yml
··· 1 + # Do not edit this file directly. It is generated by https://deno.land/x/fluent_github_actions 2 + 3 + name: ci 4 + on: 5 + push: 6 + branches: 7 + - master 8 + pull_request: 9 + branches: 10 + - master 11 + jobs: 12 + tests: 13 + runs-on: ubuntu-22.04 14 + steps: 15 + - uses: actions/checkout@v2 16 + - name: Install build dependencies 17 + run: | 18 + sudo apt-get update 19 + sudo apt-get install -y build-essential libasound2-dev protobuf-compiler 20 + - name: Setup Fluent CI 21 + uses: fluentci-io/setup-fluentci@v5 22 + - name: Run tests 23 + run: | 24 + fluentci run --wasm rust test
+1 -1
.github/workflows/release-for-mac.yml
··· 5 5 jobs: 6 6 release: 7 7 name: release x86_64-apple-darwin 8 - runs-on: macos-latest 8 + runs-on: macos-13 9 9 strategy: 10 10 fail-fast: false 11 11 matrix:
+1 -1
.github/workflows/release.yml
··· 5 5 6 6 jobs: 7 7 build: 8 - runs-on: ubuntu-latest 8 + runs-on: ubuntu-22.04 9 9 strategy: 10 10 matrix: 11 11 target:
+818 -112
Cargo.lock
··· 1 1 # This file is automatically @generated by Cargo. 2 2 # It is not intended for manual editing. 3 - version = 3 3 + version = 4 4 4 5 5 [[package]] 6 6 name = "aead" ··· 69 69 ] 70 70 71 71 [[package]] 72 + name = "aho-corasick" 73 + version = "1.1.3" 74 + source = "registry+https://github.com/rust-lang/crates.io-index" 75 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 76 + dependencies = [ 77 + "memchr", 78 + ] 79 + 80 + [[package]] 72 81 name = "allocator-api2" 73 82 version = "0.2.16" 74 83 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 97 106 ] 98 107 99 108 [[package]] 109 + name = "android-tzdata" 110 + version = "0.1.1" 111 + source = "registry+https://github.com/rust-lang/crates.io-index" 112 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 113 + 114 + [[package]] 115 + name = "android_system_properties" 116 + version = "0.1.5" 117 + source = "registry+https://github.com/rust-lang/crates.io-index" 118 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 119 + dependencies = [ 120 + "libc", 121 + ] 122 + 123 + [[package]] 100 124 name = "anyhow" 101 125 version = "1.0.69" 102 126 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 115 139 checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 116 140 117 141 [[package]] 142 + name = "async-attributes" 143 + version = "1.1.2" 144 + source = "registry+https://github.com/rust-lang/crates.io-index" 145 + checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" 146 + dependencies = [ 147 + "quote", 148 + "syn 1.0.107", 149 + ] 150 + 151 + [[package]] 118 152 name = "async-channel" 119 153 version = "1.8.0" 120 154 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 162 196 "blocking", 163 197 "futures-lite", 164 198 "once_cell", 199 + "tokio", 165 200 ] 166 201 167 202 [[package]] ··· 195 230 "parking", 196 231 "polling", 197 232 "slab", 198 - "socket2", 233 + "socket2 0.4.7", 199 234 "waker-fn", 200 235 "windows-sys 0.42.0", 201 236 ] ··· 211 246 ] 212 247 213 248 [[package]] 249 + name = "async-process" 250 + version = "1.7.0" 251 + source = "registry+https://github.com/rust-lang/crates.io-index" 252 + checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" 253 + dependencies = [ 254 + "async-io", 255 + "async-lock", 256 + "autocfg", 257 + "blocking", 258 + "cfg-if", 259 + "event-listener", 260 + "futures-lite", 261 + "rustix", 262 + "signal-hook", 263 + "windows-sys 0.48.0", 264 + ] 265 + 266 + [[package]] 214 267 name = "async-std" 215 268 version = "1.12.0" 216 269 source = "registry+https://github.com/rust-lang/crates.io-index" 217 270 checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" 218 271 dependencies = [ 272 + "async-attributes", 219 273 "async-channel", 220 274 "async-global-executor", 221 275 "async-io", 222 276 "async-lock", 277 + "async-process", 223 278 "crossbeam-utils", 224 279 "futures-channel", 225 280 "futures-core", ··· 237 292 ] 238 293 239 294 [[package]] 295 + name = "async-std-resolver" 296 + version = "0.24.2" 297 + source = "registry+https://github.com/rust-lang/crates.io-index" 298 + checksum = "fa5ee46ec0c518414838d2fdc7dd18f6ba7d934b6e728005c958621da450682d" 299 + dependencies = [ 300 + "async-std", 301 + "async-trait", 302 + "futures-io", 303 + "futures-util", 304 + "hickory-resolver", 305 + "pin-utils", 306 + "socket2 0.5.8", 307 + ] 308 + 309 + [[package]] 240 310 name = "async-stream" 241 311 version = "0.3.4" 242 312 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 273 343 "futures-core", 274 344 "futures-io", 275 345 "rustls 0.18.1", 276 - "webpki 0.21.4", 346 + "webpki", 277 347 "webpki-roots 0.20.0", 278 348 ] 279 349 280 350 [[package]] 281 351 name = "async-trait" 282 - version = "0.1.64" 352 + version = "0.1.85" 283 353 source = "registry+https://github.com/rust-lang/crates.io-index" 284 - checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" 354 + checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" 285 355 dependencies = [ 286 356 "proc-macro2", 287 357 "quote", 288 - "syn 1.0.107", 358 + "syn 2.0.51", 289 359 ] 290 360 291 361 [[package]] ··· 329 399 "matchit", 330 400 "memchr", 331 401 "mime", 332 - "percent-encoding 2.2.0", 402 + "percent-encoding 2.3.1", 333 403 "pin-project-lite", 334 404 "rustversion", 335 405 "serde", ··· 506 576 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 507 577 508 578 [[package]] 579 + name = "chrono" 580 + version = "0.4.39" 581 + source = "registry+https://github.com/rust-lang/crates.io-index" 582 + checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 583 + dependencies = [ 584 + "android-tzdata", 585 + "iana-time-zone", 586 + "js-sys", 587 + "num-traits", 588 + "serde", 589 + "wasm-bindgen", 590 + "windows-targets 0.52.6", 591 + ] 592 + 593 + [[package]] 509 594 name = "cipher" 510 595 version = "0.2.5" 511 596 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 620 705 "base64 0.13.1", 621 706 "hkdf", 622 707 "hmac", 623 - "percent-encoding 2.2.0", 708 + "percent-encoding 2.3.1", 624 709 "rand 0.8.5", 625 710 "sha2", 626 711 "time", ··· 628 713 ] 629 714 630 715 [[package]] 716 + name = "core-foundation" 717 + version = "0.9.3" 718 + source = "registry+https://github.com/rust-lang/crates.io-index" 719 + checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 720 + dependencies = [ 721 + "core-foundation-sys", 722 + "libc", 723 + ] 724 + 725 + [[package]] 631 726 name = "core-foundation-sys" 632 727 version = "0.8.3" 633 728 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 746 841 ] 747 842 748 843 [[package]] 749 - name = "ctor" 750 - version = "0.1.26" 751 - source = "registry+https://github.com/rust-lang/crates.io-index" 752 - checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 753 - dependencies = [ 754 - "quote", 755 - "syn 1.0.107", 756 - ] 757 - 758 - [[package]] 759 844 name = "ctr" 760 845 version = "0.6.0" 761 846 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 782 867 "once_cell", 783 868 "parking_lot_core", 784 869 ] 870 + 871 + [[package]] 872 + name = "data-encoding" 873 + version = "2.7.0" 874 + source = "registry+https://github.com/rust-lang/crates.io-index" 875 + checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" 785 876 786 877 [[package]] 787 878 name = "deadpool" ··· 826 917 checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 827 918 828 919 [[package]] 920 + name = "displaydoc" 921 + version = "0.2.5" 922 + source = "registry+https://github.com/rust-lang/crates.io-index" 923 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 924 + dependencies = [ 925 + "proc-macro2", 926 + "quote", 927 + "syn 2.0.51", 928 + ] 929 + 930 + [[package]] 829 931 name = "either" 830 932 version = "1.8.1" 831 933 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 841 943 ] 842 944 843 945 [[package]] 946 + name = "enum-as-inner" 947 + version = "0.6.1" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 950 + dependencies = [ 951 + "heck 0.5.0", 952 + "proc-macro2", 953 + "quote", 954 + "syn 2.0.51", 955 + ] 956 + 957 + [[package]] 958 + name = "errno" 959 + version = "0.3.10" 960 + source = "registry+https://github.com/rust-lang/crates.io-index" 961 + checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 962 + dependencies = [ 963 + "libc", 964 + "windows-sys 0.59.0", 965 + ] 966 + 967 + [[package]] 844 968 name = "event-listener" 845 969 version = "2.5.3" 846 970 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 869 993 870 994 [[package]] 871 995 name = "form_urlencoded" 872 - version = "1.1.0" 996 + version = "1.2.1" 873 997 source = "registry+https://github.com/rust-lang/crates.io-index" 874 - checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 998 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 875 999 dependencies = [ 876 - "percent-encoding 2.2.0", 1000 + "percent-encoding 2.3.1", 877 1001 ] 878 1002 879 1003 [[package]] ··· 1082 1206 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 1083 1207 1084 1208 [[package]] 1209 + name = "heck" 1210 + version = "0.5.0" 1211 + source = "registry+https://github.com/rust-lang/crates.io-index" 1212 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1213 + 1214 + [[package]] 1085 1215 name = "hermit-abi" 1086 1216 version = "0.1.19" 1087 1217 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1100 1230 ] 1101 1231 1102 1232 [[package]] 1233 + name = "hermit-abi" 1234 + version = "0.3.9" 1235 + source = "registry+https://github.com/rust-lang/crates.io-index" 1236 + checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1237 + 1238 + [[package]] 1239 + name = "hickory-proto" 1240 + version = "0.24.2" 1241 + source = "registry+https://github.com/rust-lang/crates.io-index" 1242 + checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" 1243 + dependencies = [ 1244 + "async-trait", 1245 + "cfg-if", 1246 + "data-encoding", 1247 + "enum-as-inner", 1248 + "futures-channel", 1249 + "futures-io", 1250 + "futures-util", 1251 + "idna 1.0.3", 1252 + "ipnet", 1253 + "once_cell", 1254 + "rand 0.8.5", 1255 + "thiserror", 1256 + "tinyvec", 1257 + "tracing", 1258 + "url 2.5.4", 1259 + ] 1260 + 1261 + [[package]] 1262 + name = "hickory-resolver" 1263 + version = "0.24.2" 1264 + source = "registry+https://github.com/rust-lang/crates.io-index" 1265 + checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" 1266 + dependencies = [ 1267 + "cfg-if", 1268 + "futures-util", 1269 + "hickory-proto", 1270 + "ipconfig", 1271 + "lru-cache", 1272 + "once_cell", 1273 + "parking_lot", 1274 + "rand 0.8.5", 1275 + "resolv-conf", 1276 + "smallvec", 1277 + "thiserror", 1278 + "tracing", 1279 + ] 1280 + 1281 + [[package]] 1103 1282 name = "hkdf" 1104 1283 version = "0.10.0" 1105 1284 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1117 1296 dependencies = [ 1118 1297 "crypto-mac", 1119 1298 "digest", 1299 + ] 1300 + 1301 + [[package]] 1302 + name = "hostname" 1303 + version = "0.3.1" 1304 + source = "registry+https://github.com/rust-lang/crates.io-index" 1305 + checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 1306 + dependencies = [ 1307 + "libc", 1308 + "match_cfg", 1309 + "winapi", 1120 1310 ] 1121 1311 1122 1312 [[package]] ··· 1191 1381 "serde_json", 1192 1382 "serde_qs", 1193 1383 "serde_urlencoded", 1194 - "url 2.3.1", 1384 + "url 2.5.4", 1195 1385 ] 1196 1386 1197 1387 [[package]] ··· 1223 1413 "httpdate", 1224 1414 "itoa", 1225 1415 "pin-project-lite", 1226 - "socket2", 1416 + "socket2 0.4.7", 1227 1417 "tokio", 1228 1418 "tower-service", 1229 1419 "tracing", ··· 1232 1422 1233 1423 [[package]] 1234 1424 name = "hyper-rustls" 1235 - version = "0.23.2" 1425 + version = "0.24.2" 1236 1426 source = "registry+https://github.com/rust-lang/crates.io-index" 1237 - checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" 1427 + checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" 1238 1428 dependencies = [ 1429 + "futures-util", 1239 1430 "http", 1240 1431 "hyper", 1241 - "rustls 0.20.9", 1432 + "rustls 0.21.12", 1242 1433 "tokio", 1243 1434 "tokio-rustls", 1244 1435 ] ··· 1256 1447 ] 1257 1448 1258 1449 [[package]] 1450 + name = "iana-time-zone" 1451 + version = "0.1.61" 1452 + source = "registry+https://github.com/rust-lang/crates.io-index" 1453 + checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 1454 + dependencies = [ 1455 + "android_system_properties", 1456 + "core-foundation-sys", 1457 + "iana-time-zone-haiku", 1458 + "js-sys", 1459 + "wasm-bindgen", 1460 + "windows-core", 1461 + ] 1462 + 1463 + [[package]] 1464 + name = "iana-time-zone-haiku" 1465 + version = "0.1.2" 1466 + source = "registry+https://github.com/rust-lang/crates.io-index" 1467 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1468 + dependencies = [ 1469 + "cc", 1470 + ] 1471 + 1472 + [[package]] 1473 + name = "icu_collections" 1474 + version = "1.5.0" 1475 + source = "registry+https://github.com/rust-lang/crates.io-index" 1476 + checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 1477 + dependencies = [ 1478 + "displaydoc", 1479 + "yoke", 1480 + "zerofrom", 1481 + "zerovec", 1482 + ] 1483 + 1484 + [[package]] 1485 + name = "icu_locid" 1486 + version = "1.5.0" 1487 + source = "registry+https://github.com/rust-lang/crates.io-index" 1488 + checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 1489 + dependencies = [ 1490 + "displaydoc", 1491 + "litemap", 1492 + "tinystr", 1493 + "writeable", 1494 + "zerovec", 1495 + ] 1496 + 1497 + [[package]] 1498 + name = "icu_locid_transform" 1499 + version = "1.5.0" 1500 + source = "registry+https://github.com/rust-lang/crates.io-index" 1501 + checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 1502 + dependencies = [ 1503 + "displaydoc", 1504 + "icu_locid", 1505 + "icu_locid_transform_data", 1506 + "icu_provider", 1507 + "tinystr", 1508 + "zerovec", 1509 + ] 1510 + 1511 + [[package]] 1512 + name = "icu_locid_transform_data" 1513 + version = "1.5.0" 1514 + source = "registry+https://github.com/rust-lang/crates.io-index" 1515 + checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 1516 + 1517 + [[package]] 1518 + name = "icu_normalizer" 1519 + version = "1.5.0" 1520 + source = "registry+https://github.com/rust-lang/crates.io-index" 1521 + checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 1522 + dependencies = [ 1523 + "displaydoc", 1524 + "icu_collections", 1525 + "icu_normalizer_data", 1526 + "icu_properties", 1527 + "icu_provider", 1528 + "smallvec", 1529 + "utf16_iter", 1530 + "utf8_iter", 1531 + "write16", 1532 + "zerovec", 1533 + ] 1534 + 1535 + [[package]] 1536 + name = "icu_normalizer_data" 1537 + version = "1.5.0" 1538 + source = "registry+https://github.com/rust-lang/crates.io-index" 1539 + checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 1540 + 1541 + [[package]] 1542 + name = "icu_properties" 1543 + version = "1.5.1" 1544 + source = "registry+https://github.com/rust-lang/crates.io-index" 1545 + checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 1546 + dependencies = [ 1547 + "displaydoc", 1548 + "icu_collections", 1549 + "icu_locid_transform", 1550 + "icu_properties_data", 1551 + "icu_provider", 1552 + "tinystr", 1553 + "zerovec", 1554 + ] 1555 + 1556 + [[package]] 1557 + name = "icu_properties_data" 1558 + version = "1.5.0" 1559 + source = "registry+https://github.com/rust-lang/crates.io-index" 1560 + checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 1561 + 1562 + [[package]] 1563 + name = "icu_provider" 1564 + version = "1.5.0" 1565 + source = "registry+https://github.com/rust-lang/crates.io-index" 1566 + checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 1567 + dependencies = [ 1568 + "displaydoc", 1569 + "icu_locid", 1570 + "icu_provider_macros", 1571 + "stable_deref_trait", 1572 + "tinystr", 1573 + "writeable", 1574 + "yoke", 1575 + "zerofrom", 1576 + "zerovec", 1577 + ] 1578 + 1579 + [[package]] 1580 + name = "icu_provider_macros" 1581 + version = "1.5.0" 1582 + source = "registry+https://github.com/rust-lang/crates.io-index" 1583 + checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 1584 + dependencies = [ 1585 + "proc-macro2", 1586 + "quote", 1587 + "syn 2.0.51", 1588 + ] 1589 + 1590 + [[package]] 1259 1591 name = "idna" 1260 1592 version = "0.1.5" 1261 1593 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1268 1600 1269 1601 [[package]] 1270 1602 name = "idna" 1271 - version = "0.3.0" 1603 + version = "1.0.3" 1604 + source = "registry+https://github.com/rust-lang/crates.io-index" 1605 + checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1606 + dependencies = [ 1607 + "idna_adapter", 1608 + "smallvec", 1609 + "utf8_iter", 1610 + ] 1611 + 1612 + [[package]] 1613 + name = "idna_adapter" 1614 + version = "1.2.0" 1272 1615 source = "registry+https://github.com/rust-lang/crates.io-index" 1273 - checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 1616 + checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 1274 1617 dependencies = [ 1275 - "unicode-bidi", 1276 - "unicode-normalization", 1618 + "icu_normalizer", 1619 + "icu_properties", 1277 1620 ] 1278 1621 1279 1622 [[package]] ··· 1308 1651 ] 1309 1652 1310 1653 [[package]] 1654 + name = "io-lifetimes" 1655 + version = "1.0.11" 1656 + source = "registry+https://github.com/rust-lang/crates.io-index" 1657 + checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 1658 + dependencies = [ 1659 + "hermit-abi 0.3.9", 1660 + "libc", 1661 + "windows-sys 0.48.0", 1662 + ] 1663 + 1664 + [[package]] 1665 + name = "ipconfig" 1666 + version = "0.3.2" 1667 + source = "registry+https://github.com/rust-lang/crates.io-index" 1668 + checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 1669 + dependencies = [ 1670 + "socket2 0.5.8", 1671 + "widestring", 1672 + "windows-sys 0.48.0", 1673 + "winreg", 1674 + ] 1675 + 1676 + [[package]] 1311 1677 name = "ipnet" 1312 1678 version = "2.7.1" 1313 1679 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1368 1734 1369 1735 [[package]] 1370 1736 name = "js-sys" 1371 - version = "0.3.61" 1737 + version = "0.3.77" 1372 1738 source = "registry+https://github.com/rust-lang/crates.io-index" 1373 - checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 1739 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 1374 1740 dependencies = [ 1741 + "once_cell", 1375 1742 "wasm-bindgen", 1376 1743 ] 1377 1744 ··· 1422 1789 1423 1790 [[package]] 1424 1791 name = "libc" 1425 - version = "0.2.139" 1792 + version = "0.2.169" 1426 1793 source = "registry+https://github.com/rust-lang/crates.io-index" 1427 - checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 1794 + checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 1428 1795 1429 1796 [[package]] 1430 1797 name = "libloading" ··· 1437 1804 ] 1438 1805 1439 1806 [[package]] 1807 + name = "linked-hash-map" 1808 + version = "0.5.6" 1809 + source = "registry+https://github.com/rust-lang/crates.io-index" 1810 + checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 1811 + 1812 + [[package]] 1813 + name = "linux-raw-sys" 1814 + version = "0.3.8" 1815 + source = "registry+https://github.com/rust-lang/crates.io-index" 1816 + checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 1817 + 1818 + [[package]] 1819 + name = "litemap" 1820 + version = "0.7.4" 1821 + source = "registry+https://github.com/rust-lang/crates.io-index" 1822 + checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 1823 + 1824 + [[package]] 1440 1825 name = "lock_api" 1441 1826 version = "0.4.9" 1442 1827 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1448 1833 1449 1834 [[package]] 1450 1835 name = "log" 1451 - version = "0.4.17" 1836 + version = "0.4.25" 1452 1837 source = "registry+https://github.com/rust-lang/crates.io-index" 1453 - checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 1838 + checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 1454 1839 dependencies = [ 1455 - "cfg-if", 1456 1840 "value-bag", 1457 1841 ] 1458 1842 ··· 1466 1850 ] 1467 1851 1468 1852 [[package]] 1853 + name = "lru-cache" 1854 + version = "0.1.2" 1855 + source = "registry+https://github.com/rust-lang/crates.io-index" 1856 + checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 1857 + dependencies = [ 1858 + "linked-hash-map", 1859 + ] 1860 + 1861 + [[package]] 1469 1862 name = "m3u" 1470 1863 version = "1.0.0" 1471 1864 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1484 1877 ] 1485 1878 1486 1879 [[package]] 1880 + name = "match_cfg" 1881 + version = "0.1.0" 1882 + source = "registry+https://github.com/rust-lang/crates.io-index" 1883 + checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 1884 + 1885 + [[package]] 1487 1886 name = "matches" 1488 1887 version = "0.1.10" 1489 1888 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1497 1896 1498 1897 [[package]] 1499 1898 name = "memchr" 1500 - version = "2.5.0" 1899 + version = "2.7.4" 1501 1900 source = "registry+https://github.com/rust-lang/crates.io-index" 1502 - checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 1901 + checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1503 1902 1504 1903 [[package]] 1505 1904 name = "memoffset" ··· 1844 2243 1845 2244 [[package]] 1846 2245 name = "percent-encoding" 1847 - version = "2.2.0" 2246 + version = "2.3.1" 1848 2247 source = "registry+https://github.com/rust-lang/crates.io-index" 1849 - checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 2248 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1850 2249 1851 2250 [[package]] 1852 2251 name = "petgraph" ··· 1997 2396 checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" 1998 2397 dependencies = [ 1999 2398 "bytes", 2000 - "heck", 2399 + "heck 0.4.1", 2001 2400 "itertools 0.10.5", 2002 2401 "lazy_static", 2003 2402 "log", ··· 2035 2434 ] 2036 2435 2037 2436 [[package]] 2437 + name = "quick-error" 2438 + version = "1.2.3" 2439 + source = "registry+https://github.com/rust-lang/crates.io-index" 2440 + checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 2441 + 2442 + [[package]] 2038 2443 name = "quote" 2039 2444 version = "1.0.35" 2040 2445 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2044 2449 ] 2045 2450 2046 2451 [[package]] 2452 + name = "radiobrowser" 2453 + version = "0.6.1" 2454 + source = "registry+https://github.com/rust-lang/crates.io-index" 2455 + checksum = "763ad5f847e3d2e3221dac4e87370297a970e3f9eb8f2760b915001b9c79023b" 2456 + dependencies = [ 2457 + "async-std", 2458 + "async-std-resolver", 2459 + "chrono", 2460 + "log", 2461 + "rand 0.8.5", 2462 + "reqwest", 2463 + "serde", 2464 + ] 2465 + 2466 + [[package]] 2047 2467 name = "rand" 2048 2468 version = "0.7.3" 2049 2469 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2163 2583 2164 2584 [[package]] 2165 2585 name = "regex" 2166 - version = "1.7.1" 2586 + version = "1.11.1" 2167 2587 source = "registry+https://github.com/rust-lang/crates.io-index" 2168 - checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 2588 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 2169 2589 dependencies = [ 2590 + "aho-corasick", 2591 + "memchr", 2592 + "regex-automata", 2593 + "regex-syntax", 2594 + ] 2595 + 2596 + [[package]] 2597 + name = "regex-automata" 2598 + version = "0.4.9" 2599 + source = "registry+https://github.com/rust-lang/crates.io-index" 2600 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 2601 + dependencies = [ 2602 + "aho-corasick", 2603 + "memchr", 2170 2604 "regex-syntax", 2171 2605 ] 2172 2606 2173 2607 [[package]] 2174 2608 name = "regex-syntax" 2175 - version = "0.6.28" 2609 + version = "0.8.5" 2176 2610 source = "registry+https://github.com/rust-lang/crates.io-index" 2177 - checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 2611 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 2178 2612 2179 2613 [[package]] 2180 2614 name = "remove_dir_all" ··· 2187 2621 2188 2622 [[package]] 2189 2623 name = "reqwest" 2190 - version = "0.11.14" 2624 + version = "0.11.27" 2191 2625 source = "registry+https://github.com/rust-lang/crates.io-index" 2192 - checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" 2626 + checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" 2193 2627 dependencies = [ 2194 2628 "base64 0.21.0", 2195 2629 "bytes", ··· 2206 2640 "log", 2207 2641 "mime", 2208 2642 "once_cell", 2209 - "percent-encoding 2.2.0", 2643 + "percent-encoding 2.3.1", 2210 2644 "pin-project-lite", 2211 - "rustls 0.20.9", 2645 + "rustls 0.21.12", 2212 2646 "rustls-pemfile", 2213 2647 "serde", 2214 2648 "serde_json", 2215 2649 "serde_urlencoded", 2650 + "sync_wrapper", 2651 + "system-configuration", 2216 2652 "tokio", 2217 2653 "tokio-rustls", 2218 2654 "tower-service", 2219 - "url 2.3.1", 2655 + "url 2.5.4", 2220 2656 "wasm-bindgen", 2221 2657 "wasm-bindgen-futures", 2222 2658 "web-sys", 2223 - "webpki-roots 0.22.6", 2659 + "webpki-roots 0.25.4", 2224 2660 "winreg", 2661 + ] 2662 + 2663 + [[package]] 2664 + name = "resolv-conf" 2665 + version = "0.7.0" 2666 + source = "registry+https://github.com/rust-lang/crates.io-index" 2667 + checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 2668 + dependencies = [ 2669 + "hostname", 2670 + "quick-error", 2225 2671 ] 2226 2672 2227 2673 [[package]] ··· 2312 2758 ] 2313 2759 2314 2760 [[package]] 2761 + name = "rustix" 2762 + version = "0.37.28" 2763 + source = "registry+https://github.com/rust-lang/crates.io-index" 2764 + checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" 2765 + dependencies = [ 2766 + "bitflags 1.3.2", 2767 + "errno", 2768 + "io-lifetimes", 2769 + "libc", 2770 + "linux-raw-sys", 2771 + "windows-sys 0.48.0", 2772 + ] 2773 + 2774 + [[package]] 2315 2775 name = "rustls" 2316 2776 version = "0.18.1" 2317 2777 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2321 2781 "log", 2322 2782 "ring 0.16.20", 2323 2783 "sct 0.6.1", 2324 - "webpki 0.21.4", 2784 + "webpki", 2325 2785 ] 2326 2786 2327 2787 [[package]] 2328 2788 name = "rustls" 2329 - version = "0.20.9" 2789 + version = "0.21.12" 2330 2790 source = "registry+https://github.com/rust-lang/crates.io-index" 2331 - checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" 2791 + checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" 2332 2792 dependencies = [ 2333 2793 "log", 2334 - "ring 0.16.20", 2794 + "ring 0.17.3", 2795 + "rustls-webpki", 2335 2796 "sct 0.7.1", 2336 - "webpki 0.22.4", 2337 2797 ] 2338 2798 2339 2799 [[package]] ··· 2346 2806 ] 2347 2807 2348 2808 [[package]] 2809 + name = "rustls-webpki" 2810 + version = "0.101.7" 2811 + source = "registry+https://github.com/rust-lang/crates.io-index" 2812 + checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" 2813 + dependencies = [ 2814 + "ring 0.17.3", 2815 + "untrusted 0.9.0", 2816 + ] 2817 + 2818 + [[package]] 2349 2819 name = "rustversion" 2350 2820 version = "1.0.11" 2351 2821 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2450 2920 source = "registry+https://github.com/rust-lang/crates.io-index" 2451 2921 checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" 2452 2922 dependencies = [ 2453 - "percent-encoding 2.2.0", 2923 + "percent-encoding 2.3.1", 2454 2924 "serde", 2455 2925 "thiserror", 2456 2926 ] ··· 2562 3032 2563 3033 [[package]] 2564 3034 name = "smallvec" 2565 - version = "1.10.0" 3035 + version = "1.13.2" 2566 3036 source = "registry+https://github.com/rust-lang/crates.io-index" 2567 - checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 3037 + checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 2568 3038 2569 3039 [[package]] 2570 3040 name = "socket2" ··· 2577 3047 ] 2578 3048 2579 3049 [[package]] 3050 + name = "socket2" 3051 + version = "0.5.8" 3052 + source = "registry+https://github.com/rust-lang/crates.io-index" 3053 + checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 3054 + dependencies = [ 3055 + "libc", 3056 + "windows-sys 0.52.0", 3057 + ] 3058 + 3059 + [[package]] 2580 3060 name = "spin" 2581 3061 version = "0.5.2" 2582 3062 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2597 3077 "quote", 2598 3078 "syn 1.0.107", 2599 3079 ] 3080 + 3081 + [[package]] 3082 + name = "stable_deref_trait" 3083 + version = "1.2.0" 3084 + source = "registry+https://github.com/rust-lang/crates.io-index" 3085 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2600 3086 2601 3087 [[package]] 2602 3088 name = "standback" ··· 2689 3175 source = "registry+https://github.com/rust-lang/crates.io-index" 2690 3176 checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" 2691 3177 dependencies = [ 2692 - "heck", 3178 + "heck 0.4.1", 2693 3179 "proc-macro2", 2694 3180 "quote", 2695 3181 "rustversion", ··· 2934 3420 version = "0.1.2" 2935 3421 source = "registry+https://github.com/rust-lang/crates.io-index" 2936 3422 checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 3423 + 3424 + [[package]] 3425 + name = "synstructure" 3426 + version = "0.13.1" 3427 + source = "registry+https://github.com/rust-lang/crates.io-index" 3428 + checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 3429 + dependencies = [ 3430 + "proc-macro2", 3431 + "quote", 3432 + "syn 2.0.51", 3433 + ] 3434 + 3435 + [[package]] 3436 + name = "system-configuration" 3437 + version = "0.5.1" 3438 + source = "registry+https://github.com/rust-lang/crates.io-index" 3439 + checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 3440 + dependencies = [ 3441 + "bitflags 1.3.2", 3442 + "core-foundation", 3443 + "system-configuration-sys", 3444 + ] 3445 + 3446 + [[package]] 3447 + name = "system-configuration-sys" 3448 + version = "0.5.0" 3449 + source = "registry+https://github.com/rust-lang/crates.io-index" 3450 + checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 3451 + dependencies = [ 3452 + "core-foundation-sys", 3453 + "libc", 3454 + ] 2937 3455 2938 3456 [[package]] 2939 3457 name = "tempfile" ··· 3035 3553 ] 3036 3554 3037 3555 [[package]] 3556 + name = "tinystr" 3557 + version = "0.7.6" 3558 + source = "registry+https://github.com/rust-lang/crates.io-index" 3559 + checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 3560 + dependencies = [ 3561 + "displaydoc", 3562 + "zerovec", 3563 + ] 3564 + 3565 + [[package]] 3038 3566 name = "tinyvec" 3039 3567 version = "1.6.0" 3040 3568 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3062 3590 "mio", 3063 3591 "num_cpus", 3064 3592 "pin-project-lite", 3065 - "socket2", 3593 + "socket2 0.4.7", 3066 3594 "tokio-macros", 3067 3595 "windows-sys 0.42.0", 3068 3596 ] ··· 3090 3618 3091 3619 [[package]] 3092 3620 name = "tokio-rustls" 3093 - version = "0.23.4" 3621 + version = "0.24.1" 3094 3622 source = "registry+https://github.com/rust-lang/crates.io-index" 3095 - checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 3623 + checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" 3096 3624 dependencies = [ 3097 - "rustls 0.20.9", 3625 + "rustls 0.21.12", 3098 3626 "tokio", 3099 - "webpki 0.22.4", 3100 3627 ] 3101 3628 3102 3629 [[package]] ··· 3159 3686 "http-body", 3160 3687 "hyper", 3161 3688 "hyper-timeout", 3162 - "percent-encoding 2.2.0", 3689 + "percent-encoding 2.3.1", 3163 3690 "pin-project", 3164 3691 "prost", 3165 3692 "prost-derive", ··· 3316 3843 3317 3844 [[package]] 3318 3845 name = "tunein" 3319 - version = "0.1.2" 3846 + version = "0.1.3" 3320 3847 source = "registry+https://github.com/rust-lang/crates.io-index" 3321 - checksum = "fbb48ce12eca4db1649e7d311e782e9b8a341c844cec57b5b8d1e332381e323f" 3848 + checksum = "9d33b9d790869737661ff1f34a95da32a1288373c5b78c66d098cff2df045055" 3322 3849 dependencies = [ 3323 3850 "anyhow", 3324 3851 "m3u", ··· 3334 3861 version = "0.2.6" 3335 3862 dependencies = [ 3336 3863 "anyhow", 3864 + "async-trait", 3337 3865 "clap", 3338 3866 "cpal", 3339 3867 "crossterm", ··· 3346 3874 "owo-colors", 3347 3875 "pls", 3348 3876 "prost", 3877 + "radiobrowser", 3349 3878 "ratatui", 3879 + "regex", 3350 3880 "reqwest", 3351 3881 "rodio", 3352 3882 "rustfft", ··· 3360 3890 "tonic-build", 3361 3891 "tonic-web", 3362 3892 "tunein", 3363 - "url 2.3.1", 3893 + "url 2.5.4", 3364 3894 ] 3365 3895 3366 3896 [[package]] ··· 3446 3976 3447 3977 [[package]] 3448 3978 name = "url" 3449 - version = "2.3.1" 3979 + version = "2.5.4" 3450 3980 source = "registry+https://github.com/rust-lang/crates.io-index" 3451 - checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 3981 + checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 3452 3982 dependencies = [ 3453 3983 "form_urlencoded", 3454 - "idna 0.3.0", 3455 - "percent-encoding 2.2.0", 3984 + "idna 1.0.3", 3985 + "percent-encoding 2.3.1", 3456 3986 "serde", 3457 3987 ] 3458 3988 3459 3989 [[package]] 3990 + name = "utf16_iter" 3991 + version = "1.0.5" 3992 + source = "registry+https://github.com/rust-lang/crates.io-index" 3993 + checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 3994 + 3995 + [[package]] 3996 + name = "utf8_iter" 3997 + version = "1.0.4" 3998 + source = "registry+https://github.com/rust-lang/crates.io-index" 3999 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 4000 + 4001 + [[package]] 3460 4002 name = "value-bag" 3461 - version = "1.0.0-alpha.9" 4003 + version = "1.10.0" 3462 4004 source = "registry+https://github.com/rust-lang/crates.io-index" 3463 - checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" 3464 - dependencies = [ 3465 - "ctor", 3466 - "version_check", 3467 - ] 4005 + checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" 3468 4006 3469 4007 [[package]] 3470 4008 name = "version_check" ··· 3513 4051 3514 4052 [[package]] 3515 4053 name = "wasm-bindgen" 3516 - version = "0.2.84" 4054 + version = "0.2.100" 3517 4055 source = "registry+https://github.com/rust-lang/crates.io-index" 3518 - checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 4056 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 3519 4057 dependencies = [ 3520 4058 "cfg-if", 4059 + "once_cell", 4060 + "rustversion", 3521 4061 "wasm-bindgen-macro", 3522 4062 ] 3523 4063 3524 4064 [[package]] 3525 4065 name = "wasm-bindgen-backend" 3526 - version = "0.2.84" 4066 + version = "0.2.100" 3527 4067 source = "registry+https://github.com/rust-lang/crates.io-index" 3528 - checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 4068 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 3529 4069 dependencies = [ 3530 4070 "bumpalo", 3531 4071 "log", 3532 - "once_cell", 3533 4072 "proc-macro2", 3534 4073 "quote", 3535 - "syn 1.0.107", 4074 + "syn 2.0.51", 3536 4075 "wasm-bindgen-shared", 3537 4076 ] 3538 4077 ··· 3550 4089 3551 4090 [[package]] 3552 4091 name = "wasm-bindgen-macro" 3553 - version = "0.2.84" 4092 + version = "0.2.100" 3554 4093 source = "registry+https://github.com/rust-lang/crates.io-index" 3555 - checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 4094 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 3556 4095 dependencies = [ 3557 4096 "quote", 3558 4097 "wasm-bindgen-macro-support", ··· 3560 4099 3561 4100 [[package]] 3562 4101 name = "wasm-bindgen-macro-support" 3563 - version = "0.2.84" 4102 + version = "0.2.100" 3564 4103 source = "registry+https://github.com/rust-lang/crates.io-index" 3565 - checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 4104 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 3566 4105 dependencies = [ 3567 4106 "proc-macro2", 3568 4107 "quote", 3569 - "syn 1.0.107", 4108 + "syn 2.0.51", 3570 4109 "wasm-bindgen-backend", 3571 4110 "wasm-bindgen-shared", 3572 4111 ] 3573 4112 3574 4113 [[package]] 3575 4114 name = "wasm-bindgen-shared" 3576 - version = "0.2.84" 4115 + version = "0.2.100" 3577 4116 source = "registry+https://github.com/rust-lang/crates.io-index" 3578 - checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 4117 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 4118 + dependencies = [ 4119 + "unicode-ident", 4120 + ] 3579 4121 3580 4122 [[package]] 3581 4123 name = "web-sys" ··· 3598 4140 ] 3599 4141 3600 4142 [[package]] 3601 - name = "webpki" 3602 - version = "0.22.4" 3603 - source = "registry+https://github.com/rust-lang/crates.io-index" 3604 - checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" 3605 - dependencies = [ 3606 - "ring 0.17.3", 3607 - "untrusted 0.9.0", 3608 - ] 3609 - 3610 - [[package]] 3611 4143 name = "webpki-roots" 3612 4144 version = "0.20.0" 3613 4145 source = "registry+https://github.com/rust-lang/crates.io-index" 3614 4146 checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" 3615 4147 dependencies = [ 3616 - "webpki 0.21.4", 4148 + "webpki", 3617 4149 ] 3618 4150 3619 4151 [[package]] 3620 4152 name = "webpki-roots" 3621 - version = "0.22.6" 4153 + version = "0.25.4" 3622 4154 source = "registry+https://github.com/rust-lang/crates.io-index" 3623 - checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" 3624 - dependencies = [ 3625 - "webpki 0.22.4", 3626 - ] 4155 + checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" 3627 4156 3628 4157 [[package]] 3629 4158 name = "wepoll-ffi" ··· 3644 4173 "libc", 3645 4174 "once_cell", 3646 4175 ] 4176 + 4177 + [[package]] 4178 + name = "widestring" 4179 + version = "1.1.0" 4180 + source = "registry+https://github.com/rust-lang/crates.io-index" 4181 + checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 3647 4182 3648 4183 [[package]] 3649 4184 name = "winapi" ··· 3690 4225 ] 3691 4226 3692 4227 [[package]] 4228 + name = "windows-core" 4229 + version = "0.52.0" 4230 + source = "registry+https://github.com/rust-lang/crates.io-index" 4231 + checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 4232 + dependencies = [ 4233 + "windows-targets 0.52.6", 4234 + ] 4235 + 4236 + [[package]] 3693 4237 name = "windows-sys" 3694 4238 version = "0.42.0" 3695 4239 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3723 4267 ] 3724 4268 3725 4269 [[package]] 4270 + name = "windows-sys" 4271 + version = "0.52.0" 4272 + source = "registry+https://github.com/rust-lang/crates.io-index" 4273 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 4274 + dependencies = [ 4275 + "windows-targets 0.52.6", 4276 + ] 4277 + 4278 + [[package]] 4279 + name = "windows-sys" 4280 + version = "0.59.0" 4281 + source = "registry+https://github.com/rust-lang/crates.io-index" 4282 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 4283 + dependencies = [ 4284 + "windows-targets 0.52.6", 4285 + ] 4286 + 4287 + [[package]] 3726 4288 name = "windows-targets" 3727 4289 version = "0.42.1" 3728 4290 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3753 4315 ] 3754 4316 3755 4317 [[package]] 4318 + name = "windows-targets" 4319 + version = "0.52.6" 4320 + source = "registry+https://github.com/rust-lang/crates.io-index" 4321 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 4322 + dependencies = [ 4323 + "windows_aarch64_gnullvm 0.52.6", 4324 + "windows_aarch64_msvc 0.52.6", 4325 + "windows_i686_gnu 0.52.6", 4326 + "windows_i686_gnullvm", 4327 + "windows_i686_msvc 0.52.6", 4328 + "windows_x86_64_gnu 0.52.6", 4329 + "windows_x86_64_gnullvm 0.52.6", 4330 + "windows_x86_64_msvc 0.52.6", 4331 + ] 4332 + 4333 + [[package]] 3756 4334 name = "windows_aarch64_gnullvm" 3757 4335 version = "0.42.1" 3758 4336 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3765 4343 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 3766 4344 3767 4345 [[package]] 4346 + name = "windows_aarch64_gnullvm" 4347 + version = "0.52.6" 4348 + source = "registry+https://github.com/rust-lang/crates.io-index" 4349 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4350 + 4351 + [[package]] 3768 4352 name = "windows_aarch64_msvc" 3769 4353 version = "0.37.0" 3770 4354 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3783 4367 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 3784 4368 3785 4369 [[package]] 4370 + name = "windows_aarch64_msvc" 4371 + version = "0.52.6" 4372 + source = "registry+https://github.com/rust-lang/crates.io-index" 4373 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4374 + 4375 + [[package]] 3786 4376 name = "windows_i686_gnu" 3787 4377 version = "0.37.0" 3788 4378 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3801 4391 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 3802 4392 3803 4393 [[package]] 4394 + name = "windows_i686_gnu" 4395 + version = "0.52.6" 4396 + source = "registry+https://github.com/rust-lang/crates.io-index" 4397 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4398 + 4399 + [[package]] 4400 + name = "windows_i686_gnullvm" 4401 + version = "0.52.6" 4402 + source = "registry+https://github.com/rust-lang/crates.io-index" 4403 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4404 + 4405 + [[package]] 3804 4406 name = "windows_i686_msvc" 3805 4407 version = "0.37.0" 3806 4408 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3819 4421 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 3820 4422 3821 4423 [[package]] 4424 + name = "windows_i686_msvc" 4425 + version = "0.52.6" 4426 + source = "registry+https://github.com/rust-lang/crates.io-index" 4427 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4428 + 4429 + [[package]] 3822 4430 name = "windows_x86_64_gnu" 3823 4431 version = "0.37.0" 3824 4432 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3837 4445 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 3838 4446 3839 4447 [[package]] 4448 + name = "windows_x86_64_gnu" 4449 + version = "0.52.6" 4450 + source = "registry+https://github.com/rust-lang/crates.io-index" 4451 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4452 + 4453 + [[package]] 3840 4454 name = "windows_x86_64_gnullvm" 3841 4455 version = "0.42.1" 3842 4456 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3849 4463 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 3850 4464 3851 4465 [[package]] 4466 + name = "windows_x86_64_gnullvm" 4467 + version = "0.52.6" 4468 + source = "registry+https://github.com/rust-lang/crates.io-index" 4469 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4470 + 4471 + [[package]] 3852 4472 name = "windows_x86_64_msvc" 3853 4473 version = "0.37.0" 3854 4474 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3867 4487 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 3868 4488 3869 4489 [[package]] 4490 + name = "windows_x86_64_msvc" 4491 + version = "0.52.6" 4492 + source = "registry+https://github.com/rust-lang/crates.io-index" 4493 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4494 + 4495 + [[package]] 3870 4496 name = "winreg" 3871 - version = "0.10.1" 4497 + version = "0.50.0" 3872 4498 source = "registry+https://github.com/rust-lang/crates.io-index" 3873 - checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 4499 + checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 3874 4500 dependencies = [ 3875 - "winapi", 4501 + "cfg-if", 4502 + "windows-sys 0.48.0", 4503 + ] 4504 + 4505 + [[package]] 4506 + name = "write16" 4507 + version = "1.0.0" 4508 + source = "registry+https://github.com/rust-lang/crates.io-index" 4509 + checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 4510 + 4511 + [[package]] 4512 + name = "writeable" 4513 + version = "0.5.5" 4514 + source = "registry+https://github.com/rust-lang/crates.io-index" 4515 + checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 4516 + 4517 + [[package]] 4518 + name = "yoke" 4519 + version = "0.7.5" 4520 + source = "registry+https://github.com/rust-lang/crates.io-index" 4521 + checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 4522 + dependencies = [ 4523 + "serde", 4524 + "stable_deref_trait", 4525 + "yoke-derive", 4526 + "zerofrom", 4527 + ] 4528 + 4529 + [[package]] 4530 + name = "yoke-derive" 4531 + version = "0.7.5" 4532 + source = "registry+https://github.com/rust-lang/crates.io-index" 4533 + checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 4534 + dependencies = [ 4535 + "proc-macro2", 4536 + "quote", 4537 + "syn 2.0.51", 4538 + "synstructure", 3876 4539 ] 3877 4540 3878 4541 [[package]] ··· 3894 4557 "quote", 3895 4558 "syn 2.0.51", 3896 4559 ] 4560 + 4561 + [[package]] 4562 + name = "zerofrom" 4563 + version = "0.1.5" 4564 + source = "registry+https://github.com/rust-lang/crates.io-index" 4565 + checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 4566 + dependencies = [ 4567 + "zerofrom-derive", 4568 + ] 4569 + 4570 + [[package]] 4571 + name = "zerofrom-derive" 4572 + version = "0.1.5" 4573 + source = "registry+https://github.com/rust-lang/crates.io-index" 4574 + checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 4575 + dependencies = [ 4576 + "proc-macro2", 4577 + "quote", 4578 + "syn 2.0.51", 4579 + "synstructure", 4580 + ] 4581 + 4582 + [[package]] 4583 + name = "zerovec" 4584 + version = "0.10.4" 4585 + source = "registry+https://github.com/rust-lang/crates.io-index" 4586 + checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 4587 + dependencies = [ 4588 + "yoke", 4589 + "zerofrom", 4590 + "zerovec-derive", 4591 + ] 4592 + 4593 + [[package]] 4594 + name = "zerovec-derive" 4595 + version = "0.10.3" 4596 + source = "registry+https://github.com/rust-lang/crates.io-index" 4597 + checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 4598 + dependencies = [ 4599 + "proc-macro2", 4600 + "quote", 4601 + "syn 2.0.51", 4602 + ]
+4 -1
Cargo.toml
··· 24 24 25 25 [dependencies] 26 26 anyhow = "1.0.69" 27 + async-trait = "0.1.85" 27 28 clap = "3.2.20" 28 29 cpal = "0.14.0" 29 30 crossterm = "0.27.0" ··· 36 37 owo-colors = "3.5.0" 37 38 pls = "0.2.2" 38 39 prost = "0.11.8" 40 + radiobrowser = { version = "0.6.1", features = ["default-rustls"], default-features = false } 39 41 ratatui = "0.26.1" 42 + regex = "1.11.1" 40 43 reqwest = {version = "0.11.14", features = ["blocking", "rustls-tls"], default-features = false} 41 44 rodio = {version = "0.16"} 42 45 rustfft = "6.2.0" ··· 48 51 tokio = {version = "1.24.2", features = ["tokio-macros", "macros", "rt", "rt-multi-thread"]} 49 52 tonic = "0.8.3" 50 53 tonic-web = "0.4.0" 51 - tunein = "0.1.2" 54 + tunein = "0.1.3" 52 55 url = "2.3.1" 53 56 54 57 [build-dependencies]
+7 -2
README.md
··· 19 19 <a href="LICENSE" target="./LICENSE"> 20 20 <img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg" /> 21 21 </a> 22 + <a href="https://github.com/tsirysndr/tunein-cli/actions/workflows/ci.yml"> 23 + <img src="https://github.com/tsirysndr/tunein-cli/actions/workflows/ci.yml/badge.svg" 24 + </a> 22 25 </p> 23 26 24 27 A command line interface for [TuneIn Radio](https://tunein.com).<br /> ··· 81 84 tunein <SUBCOMMAND> 82 85 83 86 OPTIONS: 84 - -h, --help Print help information 85 - -V, --version Print version information 87 + -h, --help Print help information 88 + -p, --provider <provider> The radio provider to use, can be 'tunein' or 'radiobrowser'. 89 + Default is 'tunein' [default: tunein] 90 + -V, --version Print version information 86 91 87 92 SUBCOMMANDS: 88 93 browse Browse radio stations
+27 -14
src/app.rs
··· 118 118 }, 119 119 frame, 120 120 ); 121 - render_line( 122 - "Now Playing ", 123 - &state.now_playing, 124 - Rect { 125 - x: size.x, 126 - y: size.y + 2, 127 - width: size.width, 128 - height: 1, 129 - }, 130 - frame, 131 - ); 121 + 122 + if !state.now_playing.is_empty() { 123 + render_line( 124 + "Now Playing ", 125 + &state.now_playing, 126 + Rect { 127 + x: size.x, 128 + y: size.y + 2, 129 + width: size.width, 130 + height: 1, 131 + }, 132 + frame, 133 + ); 134 + } 135 + 132 136 render_line( 133 137 "Genre ", 134 138 &state.genre, 135 139 Rect { 136 140 x: size.x, 137 - y: size.y + 3, 141 + y: match state.now_playing.is_empty() { 142 + true => size.y + 2, 143 + false => size.y + 3, 144 + }, 138 145 width: size.width, 139 146 height: 1, 140 147 }, ··· 145 152 &state.description, 146 153 Rect { 147 154 x: size.x, 148 - y: size.y + 4, 155 + y: match state.now_playing.is_empty() { 156 + true => size.y + 3, 157 + false => size.y + 4, 158 + }, 149 159 width: size.width, 150 160 height: 1, 151 161 }, ··· 156 166 &format!("{} kbps", &state.br), 157 167 Rect { 158 168 x: size.x, 159 - y: size.y + 5, 169 + y: match state.now_playing.is_empty() { 170 + true => size.y + 4, 171 + false => size.y + 5, 172 + }, 160 173 width: size.width, 161 174 height: 1, 162 175 },
+37 -69
src/browse.rs
··· 1 - use std::str::FromStr; 2 - 3 1 use anyhow::Error; 4 2 use owo_colors::OwoColorize; 5 - use tunein::{types::Category, TuneInClient}; 3 + 4 + use crate::provider::radiobrowser::Radiobrowser; 5 + use crate::provider::tunein::Tunein; 6 + use crate::provider::Provider; 6 7 7 - pub async fn exec(category: Option<&str>) -> Result<(), Error> { 8 - let client = TuneInClient::new(); 8 + pub async fn exec( 9 + category: Option<&str>, 10 + offset: u32, 11 + limit: u32, 12 + provider: &str, 13 + ) -> Result<(), Error> { 14 + let provider: Box<dyn Provider> = match provider { 15 + "tunein" => Box::new(Tunein::new()), 16 + "radiobrowser" => Box::new(Radiobrowser::new().await), 17 + _ => { 18 + return Err(anyhow::anyhow!(format!( 19 + "Unsupported provider '{}'", 20 + provider 21 + ))) 22 + } 23 + }; 9 24 10 - if category.is_some() && Category::from_str(category.unwrap_or_default()).is_err() { 11 - let id = category.unwrap_or_default(); 12 - let results = client 13 - .browse_by_id(id) 14 - .await 15 - .map_err(|e| Error::msg(e.to_string()))?; 16 - for result in results { 17 - println!("{}", result.text); 18 - if let Some(children) = result.children { 19 - for child in children { 20 - match child.playing { 25 + match category { 26 + Some(category) => { 27 + let results = provider.browse(category.to_string(), offset, limit).await?; 28 + for result in results { 29 + match result.id.is_empty() { 30 + false => match result.playing { 21 31 Some(playing) => println!( 22 32 " {} | {} | id: {}", 23 - child.text.magenta(), 33 + result.name.magenta(), 24 34 playing, 25 - child.guide_id.unwrap() 35 + result.id 26 36 ), 27 - None => { 28 - if let Some(guide_id) = child.guide_id { 29 - println!(" {} | {}", child.text.magenta(), guide_id); 30 - } 31 - } 32 - } 33 - } 34 - } 35 - } 36 - return Ok(()); 37 - } 37 + None => println!(" {} | id: {}", result.name.magenta(), result.id), 38 + }, 38 39 39 - let results = match category { 40 - Some(category) => match Category::from_str(category) { 41 - Ok(category) => client 42 - .browse(Some(category)) 43 - .await 44 - .map_err(|e| Error::msg(e.to_string()))?, 45 - Err(_) => { 46 - println!("Invalid category"); 47 - return Ok(()); 40 + true => println!("{}", result.name), 41 + } 48 42 } 49 - }, 50 - None => client 51 - .browse(None) 52 - .await 53 - .map_err(|e| Error::msg(e.to_string()))?, 54 - }; 55 - 56 - for result in results { 57 - match result.guide_id { 58 - Some(_) => println!( 59 - "{} | id: {}", 60 - result.text.magenta(), 61 - result.guide_id.unwrap() 62 - ), 63 - None => println!("{}", result.text), 64 43 } 65 - if let Some(children) = result.children { 66 - for child in children { 67 - match child.playing { 68 - Some(playing) => println!( 69 - " {} | {} | id: {}", 70 - child.text.magenta(), 71 - playing, 72 - child.guide_id.unwrap() 73 - ), 74 - None => println!( 75 - " {} | id: {}", 76 - child.text.magenta(), 77 - child.guide_id.unwrap() 78 - ), 79 - } 44 + None => { 45 + let results = provider.categories(offset, limit).await?; 46 + for result in results { 47 + println!("{}", result.magenta()); 80 48 } 81 49 } 82 - } 50 + }; 83 51 Ok(()) 84 52 }
+23 -4
src/main.rs
··· 10 10 mod music; 11 11 mod play; 12 12 mod player; 13 + mod provider; 13 14 mod search; 14 15 mod server; 16 + mod tags; 15 17 mod tui; 18 + mod types; 16 19 mod visualization; 17 20 18 21 fn cli() -> Command<'static> { ··· 29 32 30 33 A simple CLI to listen to radio stations"#, 31 34 ) 35 + .arg( 36 + arg!(-p --provider "The radio provider to use, can be 'tunein' or 'radiobrowser'. Default is 'tunein'").default_value("tunein") 37 + ) 32 38 .subcommand_required(true) 33 39 .subcommand( 34 40 Command::new("search") ··· 43 49 .subcommand( 44 50 Command::new("browse") 45 51 .about("Browse radio stations") 46 - .arg(arg!([category] "The category (category name or id) to browse")), 52 + .arg(arg!([category] "The category (category name or id) to browse")) 53 + .arg(arg!(--offset "The offset to start from").default_value("0")) 54 + .arg(arg!(--limit "The number of results to show").default_value("100")), 47 55 ) 48 56 .subcommand( 49 57 Command::new("server") ··· 59 67 match matches.subcommand() { 60 68 Some(("search", args)) => { 61 69 let query = args.value_of("query").unwrap(); 62 - search::exec(query).await?; 70 + let provider = matches.value_of("provider").unwrap(); 71 + search::exec(query, provider).await?; 63 72 } 64 73 Some(("play", args)) => { 65 74 let station = args.value_of("station").unwrap(); 66 - play::exec(station).await?; 75 + let provider = matches.value_of("provider").unwrap(); 76 + play::exec(station, provider).await?; 67 77 } 68 78 Some(("browse", args)) => { 69 79 let category = args.value_of("category"); 70 - browse::exec(category).await?; 80 + let offset = args.value_of("offset").unwrap(); 81 + let limit = args.value_of("limit").unwrap(); 82 + let provider = matches.value_of("provider").unwrap(); 83 + browse::exec( 84 + category, 85 + offset.parse::<u32>()?, 86 + limit.parse::<u32>()?, 87 + provider, 88 + ) 89 + .await?; 71 90 } 72 91 Some(("server", args)) => { 73 92 let port = args.value_of("port").unwrap();
+20 -46
src/play.rs
··· 2 2 3 3 use anyhow::Error; 4 4 use hyper::header::HeaderValue; 5 - use tunein::TuneInClient; 6 5 7 6 use crate::{ 8 7 app::{App, State}, 9 8 cfg::{SourceOptions, UiOptions}, 10 9 decoder::Mp3Decoder, 11 - extract::{extract_stream_url, get_currently_playing}, 10 + provider::{radiobrowser::Radiobrowser, tunein::Tunein, Provider}, 12 11 tui, 13 12 }; 14 13 15 - pub async fn exec(name_or_id: &str) -> Result<(), Error> { 16 - let client = TuneInClient::new(); 17 - let results = client 18 - .get_station(name_or_id) 19 - .await 20 - .map_err(|e| Error::msg(e.to_string()))?; 21 - let (url, playlist_type, _, id) = match results.is_empty() { 22 - true => { 23 - let results = client 24 - .search(name_or_id) 25 - .await 26 - .map_err(|e| Error::msg(e.to_string()))?; 27 - match results.first() { 28 - Some(result) => { 29 - if result.r#type != Some("audio".to_string()) { 30 - return Err(Error::msg("No station found")); 31 - } 32 - let id = result.guide_id.as_ref().unwrap(); 33 - let station = client 34 - .get_station(id) 35 - .await 36 - .map_err(|e| Error::msg(e.to_string()))?; 37 - let station = station.first().unwrap(); 38 - ( 39 - station.url.clone(), 40 - station.playlist_type.clone(), 41 - station.media_type.clone(), 42 - id.clone(), 43 - ) 44 - } 45 - None => ("".to_string(), None, "".to_string(), "".to_string()), 46 - } 47 - } 48 - false => { 49 - let result = results.first().unwrap(); 50 - ( 51 - result.url.clone(), 52 - result.playlist_type.clone(), 53 - result.media_type.clone(), 54 - name_or_id.to_string(), 55 - ) 14 + pub async fn exec(name_or_id: &str, provider: &str) -> Result<(), Error> { 15 + let _provider = provider; 16 + let provider: Box<dyn Provider> = match provider { 17 + "tunein" => Box::new(Tunein::new()), 18 + "radiobrowser" => Box::new(Radiobrowser::new().await), 19 + _ => { 20 + return Err(anyhow::anyhow!(format!( 21 + "Unsupported provider '{}'", 22 + provider 23 + ))) 56 24 } 57 25 }; 58 - let now_playing = get_currently_playing(&id).await?; 59 - let stream_url = extract_stream_url(&url, playlist_type).await?; 60 - println!("{}", stream_url); 26 + let station = provider.get_station(name_or_id.to_string()).await?; 27 + if station.is_none() { 28 + return Err(Error::msg("No station found")); 29 + } 30 + 31 + let station = station.unwrap(); 32 + let stream_url = station.stream_url.clone(); 33 + let id = station.id.clone(); 34 + let now_playing = station.playing.clone().unwrap_or_default(); 61 35 62 36 let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<State>(); 63 37 let (frame_tx, frame_rx) = std::sync::mpsc::channel::<minimp3::Frame>();
+28
src/provider/mod.rs
··· 1 + pub mod radiobrowser; 2 + pub mod tunein; 3 + 4 + use crate::types::Station; 5 + use anyhow::Error; 6 + use async_trait::async_trait; 7 + use regex::Regex; 8 + 9 + #[async_trait] 10 + pub trait Provider { 11 + async fn search(&self, name: String) -> Result<Vec<Station>, Error>; 12 + async fn get_station(&self, id: String) -> Result<Option<Station>, Error>; 13 + async fn browse( 14 + &self, 15 + category: String, 16 + offset: u32, 17 + limit: u32, 18 + ) -> Result<Vec<Station>, Error>; 19 + async fn categories(&self, offset: u32, limit: u32) -> Result<Vec<String>, Error>; 20 + } 21 + 22 + pub fn is_valid_uuid(uuid: &str) -> bool { 23 + let uuid_pattern = Regex::new( 24 + r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" 25 + ).unwrap(); 26 + 27 + uuid_pattern.is_match(uuid) 28 + }
+161
src/provider/radiobrowser.rs
··· 1 + use crate::types::Station; 2 + 3 + use super::{is_valid_uuid, Provider}; 4 + use anyhow::Error; 5 + use async_trait::async_trait; 6 + use radiobrowser::{ApiStation, RadioBrowserAPI}; 7 + use std::process::exit; 8 + 9 + pub struct Radiobrowser { 10 + client: RadioBrowserAPI, 11 + } 12 + 13 + impl Radiobrowser { 14 + pub async fn new() -> Self { 15 + let client = RadioBrowserAPI::new().await; 16 + 17 + if client.is_err() { 18 + eprintln!("Failed to create a RadioBrowserAPI client"); 19 + exit(1); 20 + } 21 + 22 + let client = client.unwrap(); 23 + Self { client } 24 + } 25 + } 26 + 27 + #[async_trait] 28 + impl Provider for Radiobrowser { 29 + async fn search(&self, name: String) -> Result<Vec<Station>, Error> { 30 + let stations = self 31 + .client 32 + .get_stations() 33 + .name(&name) 34 + .send() 35 + .await 36 + .map_err(|e| anyhow::anyhow!(format!("{}", e)))?; 37 + let stations = stations.into_iter().map(|x| Station::from(x)).collect(); 38 + Ok(stations) 39 + } 40 + 41 + async fn get_station(&self, name_or_uuid: String) -> Result<Option<Station>, Error> { 42 + match is_valid_uuid(&name_or_uuid) { 43 + true => { 44 + let servers = RadioBrowserAPI::get_default_servers() 45 + .await 46 + .map_err(|e| anyhow::anyhow!(format!("{}", e)))?; 47 + 48 + if servers.is_empty() { 49 + return Ok(None); 50 + } 51 + 52 + let client = reqwest::Client::new(); 53 + let url = format!( 54 + "https://{}/json/stations/byuuid/{}", 55 + servers[0], name_or_uuid 56 + ); 57 + let results = client 58 + .get(&url) 59 + .send() 60 + .await? 61 + .json::<Vec<ApiStation>>() 62 + .await?; 63 + 64 + Ok(results.into_iter().next().map(|x| Station::from(x))) 65 + } 66 + false => { 67 + let stations = self 68 + .client 69 + .get_stations() 70 + .name(&name_or_uuid) 71 + .name_exact(true) 72 + .send() 73 + .await 74 + .map_err(|e| anyhow::anyhow!(format!("{}", e)))?; 75 + match stations.len() { 76 + 0 => Ok(None), 77 + _ => Ok(Some(Station::from(stations[0].clone()))), 78 + } 79 + } 80 + } 81 + } 82 + 83 + async fn browse( 84 + &self, 85 + category: String, 86 + offset: u32, 87 + limit: u32, 88 + ) -> Result<Vec<Station>, Error> { 89 + let stations = self 90 + .client 91 + .get_stations() 92 + .tag(&category) 93 + .offset(&format!("{}", offset)) 94 + .limit(&format!("{}", limit)) 95 + .send() 96 + .await 97 + .map_err(|e| anyhow::anyhow!(format!("{}", e)))?; 98 + let stations = stations.into_iter().map(|x| Station::from(x)).collect(); 99 + Ok(stations) 100 + } 101 + 102 + async fn categories(&self, offset: u32, limit: u32) -> Result<Vec<String>, Error> { 103 + let categories = self 104 + .client 105 + .get_tags() 106 + .offset(&format!("{}", offset)) 107 + .limit(&format!("{}", limit)) 108 + .send() 109 + .await 110 + .map_err(|e| anyhow::anyhow!(format!("{}", e)))?; 111 + let categories = categories.into_iter().map(|x| x.name).collect(); 112 + Ok(categories) 113 + } 114 + } 115 + 116 + #[cfg(test)] 117 + mod tests { 118 + use super::*; 119 + 120 + #[tokio::test] 121 + pub async fn test_search() { 122 + let provider = Radiobrowser::new().await; 123 + let name = "alternativeradio"; 124 + let stations = provider.search(name.to_string()).await.unwrap(); 125 + assert!(stations.len() == 1) 126 + } 127 + 128 + #[tokio::test] 129 + pub async fn test_get_station() { 130 + let provider = Radiobrowser::new().await; 131 + let name = "AlternativeRadio.us".to_string(); 132 + let station = provider.get_station(name).await.unwrap(); 133 + assert!(station.is_some()) 134 + } 135 + 136 + #[tokio::test] 137 + pub async fn test_get_station_by_uuid() { 138 + let provider = Radiobrowser::new().await; 139 + let name = "964da563-0601-11e8-ae97-52543be04c81".to_string(); 140 + let station = provider.get_station(name).await.unwrap(); 141 + assert!(station.is_some()) 142 + } 143 + 144 + #[tokio::test] 145 + pub async fn test_browse() { 146 + let provider = Radiobrowser::new().await; 147 + let stations = provider.browse("music".to_string(), 0, 100).await.unwrap(); 148 + let stations = stations 149 + .into_iter() 150 + .map(|x| Station::from(x)) 151 + .collect::<Vec<Station>>(); 152 + assert!(stations.len() == 100) 153 + } 154 + 155 + #[tokio::test] 156 + pub async fn test_categories() { 157 + let provider = Radiobrowser::new().await; 158 + let categories = provider.categories(0, 100).await.unwrap(); 159 + assert!(categories.len() > 0) 160 + } 161 + }
+181
src/provider/tunein.rs
··· 1 + use crate::types::Station; 2 + 3 + use super::Provider; 4 + use anyhow::Error; 5 + use async_trait::async_trait; 6 + use tunein::TuneInClient; 7 + 8 + pub struct Tunein { 9 + client: TuneInClient, 10 + } 11 + 12 + impl Tunein { 13 + pub fn new() -> Self { 14 + Self { 15 + client: TuneInClient::new(), 16 + } 17 + } 18 + } 19 + 20 + #[async_trait] 21 + impl Provider for Tunein { 22 + async fn search(&self, name: String) -> Result<Vec<Station>, Error> { 23 + let results = self 24 + .client 25 + .search(&name) 26 + .await 27 + .map_err(|e| Error::msg(e.to_string()))?; 28 + let stations = results.into_iter().map(|x| Station::from(x)).collect(); 29 + Ok(stations) 30 + } 31 + 32 + async fn get_station(&self, id: String) -> Result<Option<Station>, Error> { 33 + let stations = self 34 + .client 35 + .get_station(&id) 36 + .await 37 + .map_err(|e| Error::msg(e.to_string()))?; 38 + match stations.len() { 39 + 0 => { 40 + let results = self.search(id.clone()).await?; 41 + let station = results.first().cloned(); 42 + match station { 43 + Some(st) => { 44 + let stations = self 45 + .client 46 + .get_station(&st.id) 47 + .await 48 + .map_err(|e| Error::msg(e.to_string()))?; 49 + let mut station = Station::from(stations[0].clone()); 50 + station.id = st.id.clone(); 51 + station.name = st.name.clone(); 52 + station.playing = st.playing.clone(); 53 + return Ok(Some(station)); 54 + } 55 + None => Ok(None), 56 + } 57 + } 58 + _ => Ok(Some(Station::from(stations[0].clone()))), 59 + } 60 + } 61 + 62 + async fn browse( 63 + &self, 64 + category: String, 65 + _offset: u32, 66 + _limit: u32, 67 + ) -> Result<Vec<Station>, Error> { 68 + let guide_id = category.clone(); 69 + let category = match category.to_lowercase().as_str() { 70 + "by location" => Some(tunein::types::Category::ByLocation), 71 + "by language" => Some(tunein::types::Category::ByLanguage), 72 + "sports" => Some(tunein::types::Category::Sports), 73 + "talk" => Some(tunein::types::Category::Talk), 74 + "music" => Some(tunein::types::Category::Music), 75 + "local radio" => Some(tunein::types::Category::LocalRadio), 76 + "podcasts" => Some(tunein::types::Category::Podcasts), 77 + _ => None, 78 + }; 79 + 80 + if category.is_none() { 81 + let category_stations = self 82 + .client 83 + .browse_by_id(&guide_id) 84 + .await 85 + .map_err(|e| Error::msg(e.to_string()))?; 86 + 87 + let mut stations = vec![]; 88 + 89 + for st in category_stations { 90 + if let Some(children) = st.clone().children { 91 + stations = [stations, vec![Box::new(st.clone())], children].concat(); 92 + } 93 + } 94 + 95 + let stations = stations.into_iter().map(|x| Station::from(x)).collect(); 96 + return Ok(stations); 97 + } 98 + 99 + let category_stations = self 100 + .client 101 + .browse(category) 102 + .await 103 + .map_err(|e| Error::msg(e.to_string()))?; 104 + 105 + let stations = category_stations 106 + .clone() 107 + .into_iter() 108 + .map(|x| Station::from(x)) 109 + .collect::<Vec<Station>>(); 110 + 111 + let mut _stations = vec![]; 112 + for st in category_stations { 113 + if let Some(children) = st.children { 114 + _stations = [_stations, children].concat(); 115 + } 116 + } 117 + let _stations = _stations 118 + .into_iter() 119 + .map(|x| Station::from(x)) 120 + .collect::<Vec<Station>>(); 121 + 122 + Ok([stations, _stations].concat()) 123 + } 124 + 125 + async fn categories(&self, _offset: u32, _limit: u32) -> Result<Vec<String>, Error> { 126 + let categories = self 127 + .client 128 + .browse(None) 129 + .await 130 + .map_err(|e| Error::msg(e.to_string()))?; 131 + let categories = categories.into_iter().map(|x| x.text).collect(); 132 + Ok(categories) 133 + } 134 + } 135 + 136 + #[cfg(test)] 137 + mod tests { 138 + use super::*; 139 + 140 + #[tokio::test] 141 + pub async fn test_search() { 142 + let provider = Tunein::new(); 143 + let name = "alternativeradio"; 144 + let stations = provider.search(name.to_string()).await.unwrap(); 145 + println!("Search: {:#?}", stations); 146 + assert!(stations.len() > 0) 147 + } 148 + 149 + #[tokio::test] 150 + pub async fn test_get_station() { 151 + let provider = Tunein::new(); 152 + let name = "s288303".to_string(); 153 + let station = provider.get_station(name).await.unwrap(); 154 + println!("Station: {:#?}", station); 155 + assert!(station.is_some()) 156 + } 157 + 158 + #[tokio::test] 159 + pub async fn test_browse() { 160 + let provider = Tunein::new(); 161 + let stations = provider.browse("music".to_string(), 0, 100).await.unwrap(); 162 + println!("Browse: {:#?}", stations); 163 + assert!(stations.len() > 0) 164 + } 165 + 166 + #[tokio::test] 167 + pub async fn test_browse_by_id() { 168 + let provider = Tunein::new(); 169 + let stations = provider.browse("c57942".to_string(), 0, 100).await.unwrap(); 170 + println!("Browse by category id: {:#?}", stations); 171 + assert!(stations.len() > 0) 172 + } 173 + 174 + #[tokio::test] 175 + pub async fn test_categories() { 176 + let provider = Tunein::new(); 177 + let categories = provider.categories(0, 100).await.unwrap(); 178 + println!("Categories: {:#?}", categories); 179 + assert!(categories.len() > 0) 180 + } 181 + }
+21 -18
src/search.rs
··· 1 1 use anyhow::Error; 2 2 use owo_colors::OwoColorize; 3 - use tunein::TuneInClient; 4 3 5 - pub async fn exec(query: &str) -> Result<(), Error> { 6 - let client = TuneInClient::new(); 7 - let results = client 8 - .search(query) 9 - .await 10 - .map_err(|e| Error::msg(e.to_string()))?; 4 + use crate::provider::{radiobrowser::Radiobrowser, tunein::Tunein, Provider}; 5 + 6 + pub async fn exec(query: &str, provider: &str) -> Result<(), Error> { 7 + let provider: Box<dyn Provider> = match provider { 8 + "tunein" => Box::new(Tunein::new()), 9 + "radiobrowser" => Box::new(Radiobrowser::new().await), 10 + _ => { 11 + return Err(anyhow::anyhow!(format!( 12 + "Unsupported provider '{}'", 13 + provider 14 + ))) 15 + } 16 + }; 17 + let results = provider.search(query.to_string()).await?; 11 18 let query = format!("\"{}\"", query); 12 19 println!("Results for {}:", query.bright_green()); 13 20 14 - let results = results 15 - .into_iter() 16 - .filter(|r| Some("audio".to_string()) == r.r#type) 17 - .collect::<Vec<_>>(); 18 - 19 21 if results.is_empty() { 20 22 println!("No results found"); 21 23 return Ok(()); 22 24 } 23 25 24 26 for result in results { 25 - if Some("audio".to_string()) == result.r#type { 26 - println!( 27 + match result.playing { 28 + Some(playing) => println!( 27 29 "{} | {} | id: {}", 28 - result.text.magenta(), 29 - result.subtext.unwrap_or_default(), 30 - result.guide_id.unwrap() 31 - ); 30 + result.name.magenta(), 31 + playing, 32 + result.id 33 + ), 34 + None => println!("{} | id: {}", result.name.magenta(), result.id), 32 35 } 33 36 } 34 37 Ok(())
+36
src/tags.rs
··· 1 + pub const TAGS: &[&str] = &[ 2 + "pop", 3 + "music", 4 + "news", 5 + "rock", 6 + "classical", 7 + "talk", 8 + "hits", 9 + "radio", 10 + "entretenimiento", 11 + "dance", 12 + "oldies", 13 + "estaciรณn", 14 + "80s", 15 + "mรฉxico", 16 + "fm", 17 + "christian", 18 + "public radio", 19 + "jazz", 20 + "mรบsica", 21 + "top 40", 22 + "classic hits", 23 + "90s", 24 + "community radio", 25 + "norteamรฉrica", 26 + "electronic", 27 + "pop music", 28 + "adult contemporary", 29 + "moi merino", 30 + "classic rock", 31 + "latinoamรฉrica", 32 + "espaรฑol", 33 + "country", 34 + "local news", 35 + "alternative", 36 + ];
+128
src/types.rs
··· 1 + use std::thread; 2 + 3 + use radiobrowser::ApiStation; 4 + use tunein::types::{SearchResult, StationLinkDetails}; 5 + 6 + use crate::extract::extract_stream_url; 7 + 8 + #[derive(Debug, Clone)] 9 + pub struct Station { 10 + pub id: String, 11 + pub name: String, 12 + pub codec: String, 13 + pub bitrate: u32, 14 + pub stream_url: String, 15 + pub playing: Option<String>, 16 + } 17 + 18 + impl From<ApiStation> for Station { 19 + fn from(station: ApiStation) -> Station { 20 + Station { 21 + id: station.stationuuid, 22 + name: station.name, 23 + codec: station.codec, 24 + bitrate: station.bitrate, 25 + stream_url: station.url_resolved, 26 + playing: None, 27 + } 28 + } 29 + } 30 + 31 + impl From<SearchResult> for Station { 32 + fn from(result: SearchResult) -> Station { 33 + Station { 34 + id: result.guide_id.unwrap_or_default(), 35 + name: result.text, 36 + bitrate: result 37 + .bitrate 38 + .unwrap_or("0".to_string()) 39 + .parse() 40 + .unwrap_or_default(), 41 + codec: Default::default(), 42 + stream_url: Default::default(), 43 + playing: result.subtext, 44 + } 45 + } 46 + } 47 + 48 + impl From<Box<SearchResult>> for Station { 49 + fn from(result: Box<SearchResult>) -> Station { 50 + Station { 51 + id: result.guide_id.unwrap_or_default(), 52 + name: result.text, 53 + bitrate: result 54 + .bitrate 55 + .unwrap_or("0".to_string()) 56 + .parse() 57 + .unwrap_or_default(), 58 + codec: Default::default(), 59 + stream_url: Default::default(), 60 + playing: None, 61 + } 62 + } 63 + } 64 + 65 + impl From<StationLinkDetails> for Station { 66 + fn from(details: StationLinkDetails) -> Station { 67 + let handle = thread::spawn(move || { 68 + let rt = tokio::runtime::Runtime::new().unwrap(); 69 + rt.block_on(extract_stream_url(&details.url, details.playlist_type)) 70 + }); 71 + let stream_url = handle.join().unwrap().unwrap(); 72 + Station { 73 + id: Default::default(), 74 + name: Default::default(), 75 + bitrate: details.bitrate, 76 + stream_url, 77 + codec: details.media_type.to_uppercase(), 78 + playing: None, 79 + } 80 + } 81 + } 82 + 83 + impl From<tunein::types::Station> for Station { 84 + fn from(st: tunein::types::Station) -> Station { 85 + Station { 86 + id: st.guide_id.unwrap_or_default(), 87 + name: st.text, 88 + bitrate: st 89 + .bitrate 90 + .unwrap_or("0".to_string()) 91 + .parse() 92 + .unwrap_or_default(), 93 + stream_url: Default::default(), 94 + codec: st.formats.unwrap_or_default().to_uppercase(), 95 + playing: st.playing, 96 + } 97 + } 98 + } 99 + 100 + impl From<Box<tunein::types::Station>> for Station { 101 + fn from(st: Box<tunein::types::Station>) -> Station { 102 + Station { 103 + id: st.guide_id.unwrap_or_default(), 104 + name: st.text, 105 + bitrate: st 106 + .bitrate 107 + .unwrap_or("0".to_string()) 108 + .parse() 109 + .unwrap_or_default(), 110 + stream_url: Default::default(), 111 + codec: st.formats.unwrap_or_default().to_uppercase(), 112 + playing: st.playing, 113 + } 114 + } 115 + } 116 + 117 + impl From<tunein::types::CategoryDetails> for Station { 118 + fn from(ct: tunein::types::CategoryDetails) -> Station { 119 + Station { 120 + id: ct.guide_id.unwrap_or_default(), 121 + name: ct.text, 122 + bitrate: 0, 123 + stream_url: Default::default(), 124 + codec: Default::default(), 125 + playing: None, 126 + } 127 + } 128 + }