A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz

feat(feed): implement feed service #8

closed opened by tsiry-sandratraina.com targeting main from feat/feed-generator
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:7vdlgi2bflelz7mmuxoqjfcr/sh.tangled.repo.pull/3lztmzurcri22
+966 -19
Diff #0
+629 -19
Cargo.lock
··· 417 417 418 418 [[package]] 419 419 name = "anyhow" 420 - version = "1.0.98" 420 + version = "1.0.100" 421 421 source = "registry+https://github.com/rust-lang/crates.io-index" 422 - checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 422 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 423 423 424 424 [[package]] 425 425 name = "arc-swap" ··· 607 607 ] 608 608 609 609 [[package]] 610 + name = "async-compression" 611 + version = "0.4.31" 612 + source = "registry+https://github.com/rust-lang/crates.io-index" 613 + checksum = "9611ec0b6acea03372540509035db2f7f1e9f04da5d27728436fa994033c00a0" 614 + dependencies = [ 615 + "compression-codecs", 616 + "compression-core", 617 + "futures-core", 618 + "pin-project-lite", 619 + "tokio", 620 + ] 621 + 622 + [[package]] 623 + name = "async-lock" 624 + version = "3.4.1" 625 + source = "registry+https://github.com/rust-lang/crates.io-index" 626 + checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" 627 + dependencies = [ 628 + "event-listener", 629 + "event-listener-strategy", 630 + "pin-project-lite", 631 + ] 632 + 633 + [[package]] 610 634 name = "async-nats" 611 635 version = "0.39.0" 612 636 ··· 707 731 checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 708 732 709 733 [[package]] 734 + name = "atrium-api" 735 + version = "0.25.5" 736 + source = "registry+https://github.com/rust-lang/crates.io-index" 737 + checksum = "8bceed6a87be6213040945254aa94191490fea22b7f49dec584e349f336cc17a" 738 + dependencies = [ 739 + "atrium-common", 740 + "atrium-xrpc", 741 + "chrono", 742 + "http 1.3.1", 743 + "ipld-core", 744 + "langtag", 745 + "regex", 746 + "serde", 747 + "serde_bytes", 748 + "serde_json", 749 + "thiserror 1.0.69", 750 + "tokio", 751 + "trait-variant", 752 + ] 753 + 754 + [[package]] 755 + name = "atrium-common" 756 + version = "0.1.2" 757 + source = "registry+https://github.com/rust-lang/crates.io-index" 758 + checksum = "9ed5610654043faa396a5a15afac0ac646d76aebe45aebd7cef4f8b96b0ab7f4" 759 + dependencies = [ 760 + "dashmap", 761 + "lru", 762 + "moka", 763 + "thiserror 1.0.69", 764 + "tokio", 765 + "trait-variant", 766 + "web-time", 767 + ] 768 + 769 + [[package]] 770 + name = "atrium-xrpc" 771 + version = "0.12.3" 772 + source = "registry+https://github.com/rust-lang/crates.io-index" 773 + checksum = "0216ad50ce34e9ff982e171c3659e65dedaa2ed5ac2994524debdc9a9647ffa8" 774 + dependencies = [ 775 + "http 1.3.1", 776 + "serde", 777 + "serde_html_form", 778 + "serde_json", 779 + "thiserror 1.0.69", 780 + "trait-variant", 781 + ] 782 + 783 + [[package]] 784 + name = "atrium-xrpc-client" 785 + version = "0.5.14" 786 + source = "registry+https://github.com/rust-lang/crates.io-index" 787 + checksum = "e099e5171f79faef52364ef0657a4cab086a71b384a779a29597a91b780de0d5" 788 + dependencies = [ 789 + "atrium-xrpc", 790 + "reqwest", 791 + ] 792 + 793 + [[package]] 710 794 name = "attohttpc" 711 795 version = "0.28.5" 712 796 ··· 769 853 ] 770 854 771 855 [[package]] 856 + name = "base-x" 857 + version = "0.2.11" 858 + source = "registry+https://github.com/rust-lang/crates.io-index" 859 + checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 860 + 861 + [[package]] 772 862 name = "base64" 773 863 version = "0.20.0" 774 864 ··· 1120 1210 ] 1121 1211 1122 1212 [[package]] 1213 + name = "cid" 1214 + version = "0.11.1" 1215 + source = "registry+https://github.com/rust-lang/crates.io-index" 1216 + checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 1217 + dependencies = [ 1218 + "core2", 1219 + "multibase", 1220 + "multihash", 1221 + "serde", 1222 + "serde_bytes", 1223 + "unsigned-varint", 1224 + ] 1225 + 1226 + [[package]] 1123 1227 name = "cipher" 1124 1228 version = "0.4.4" 1125 1229 ··· 1247 1351 ] 1248 1352 1249 1353 [[package]] 1354 + name = "compression-codecs" 1355 + version = "0.4.30" 1356 + source = "registry+https://github.com/rust-lang/crates.io-index" 1357 + checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" 1358 + dependencies = [ 1359 + "compression-core", 1360 + "flate2", 1361 + "memchr", 1362 + ] 1363 + 1364 + [[package]] 1365 + name = "compression-core" 1366 + version = "0.4.29" 1367 + source = "registry+https://github.com/rust-lang/crates.io-index" 1368 + checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" 1369 + 1370 + [[package]] 1250 1371 name = "concurrent-queue" 1251 1372 version = "2.5.0" 1252 1373 ··· 1341 1462 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 1342 1463 1343 1464 [[package]] 1465 + name = "core2" 1466 + version = "0.4.0" 1467 + source = "registry+https://github.com/rust-lang/crates.io-index" 1468 + checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" 1469 + dependencies = [ 1470 + "memchr", 1471 + ] 1472 + 1473 + [[package]] 1344 1474 name = "cpufeatures" 1345 1475 version = "0.2.17" 1346 1476 ··· 1491 1621 ] 1492 1622 1493 1623 [[package]] 1624 + name = "dashmap" 1625 + version = "6.1.0" 1626 + source = "registry+https://github.com/rust-lang/crates.io-index" 1627 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 1628 + dependencies = [ 1629 + "cfg-if", 1630 + "crossbeam-utils", 1631 + "hashbrown 0.14.5", 1632 + "lock_api", 1633 + "once_cell", 1634 + "parking_lot_core", 1635 + ] 1636 + 1637 + [[package]] 1494 1638 name = "data-encoding" 1495 1639 version = "2.9.0" 1496 1640 source = "registry+https://github.com/rust-lang/crates.io-index" 1497 1641 checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 1498 1642 1499 1643 [[package]] 1644 + name = "data-encoding-macro" 1645 + version = "0.1.18" 1646 + source = "registry+https://github.com/rust-lang/crates.io-index" 1647 + checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" 1648 + dependencies = [ 1649 + "data-encoding", 1650 + "data-encoding-macro-internal", 1651 + ] 1652 + 1653 + [[package]] 1654 + name = "data-encoding-macro-internal" 1655 + version = "0.1.16" 1656 + source = "registry+https://github.com/rust-lang/crates.io-index" 1657 + checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 1658 + dependencies = [ 1659 + "data-encoding", 1660 + "syn 2.0.101", 1661 + ] 1662 + 1663 + [[package]] 1500 1664 name = "der" 1501 1665 version = "0.7.10" 1502 1666 ··· 1780 1944 ] 1781 1945 1782 1946 [[package]] 1947 + name = "event-listener-strategy" 1948 + version = "0.5.4" 1949 + source = "registry+https://github.com/rust-lang/crates.io-index" 1950 + checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 1951 + dependencies = [ 1952 + "event-listener", 1953 + "pin-project-lite", 1954 + ] 1955 + 1956 + [[package]] 1783 1957 name = "extended" 1784 1958 version = "0.1.0" 1785 1959 ··· 1867 2041 dependencies = [ 1868 2042 "futures-core", 1869 2043 "futures-sink", 2044 + "nanorand", 1870 2045 "spin", 1871 2046 ] 1872 2047 ··· 1883 2058 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1884 2059 1885 2060 [[package]] 2061 + name = "foreign-types" 2062 + version = "0.3.2" 2063 + source = "registry+https://github.com/rust-lang/crates.io-index" 2064 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 2065 + dependencies = [ 2066 + "foreign-types-shared", 2067 + ] 2068 + 2069 + [[package]] 2070 + name = "foreign-types-shared" 2071 + version = "0.1.1" 2072 + source = "registry+https://github.com/rust-lang/crates.io-index" 2073 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 2074 + 2075 + [[package]] 1886 2076 name = "form_urlencoded" 1887 2077 version = "1.2.1" 1888 2078 ··· 2229 2419 ] 2230 2420 2231 2421 [[package]] 2422 + name = "headers" 2423 + version = "0.3.9" 2424 + source = "registry+https://github.com/rust-lang/crates.io-index" 2425 + checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" 2426 + dependencies = [ 2427 + "base64 0.21.7", 2428 + "bytes", 2429 + "headers-core", 2430 + "http 0.2.12", 2431 + "httpdate", 2432 + "mime", 2433 + "sha1", 2434 + ] 2435 + 2436 + [[package]] 2437 + name = "headers-core" 2438 + version = "0.2.0" 2439 + source = "registry+https://github.com/rust-lang/crates.io-index" 2440 + checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" 2441 + dependencies = [ 2442 + "http 0.2.12", 2443 + ] 2444 + 2445 + [[package]] 2232 2446 name = "heapless" 2233 2447 version = "0.8.0" 2234 2448 ··· 2378 2592 "futures-channel", 2379 2593 "futures-core", 2380 2594 "futures-util", 2595 + "h2 0.3.26", 2381 2596 "http 0.2.12", 2382 2597 "http-body 0.4.6", 2383 2598 "httparse", ··· 2444 2659 ] 2445 2660 2446 2661 [[package]] 2662 + name = "hyper-tls" 2663 + version = "0.6.0" 2664 + source = "registry+https://github.com/rust-lang/crates.io-index" 2665 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 2666 + dependencies = [ 2667 + "bytes", 2668 + "http-body-util", 2669 + "hyper 1.6.0", 2670 + "hyper-util", 2671 + "native-tls", 2672 + "tokio", 2673 + "tokio-native-tls", 2674 + "tower-service", 2675 + ] 2676 + 2677 + [[package]] 2447 2678 name = "hyper-util" 2448 2679 version = "0.1.12" 2449 2680 ··· 2627 2858 ] 2628 2859 2629 2860 [[package]] 2861 + name = "ipld-core" 2862 + version = "0.4.2" 2863 + source = "registry+https://github.com/rust-lang/crates.io-index" 2864 + checksum = "104718b1cc124d92a6d01ca9c9258a7df311405debb3408c445a36452f9bf8db" 2865 + dependencies = [ 2866 + "cid", 2867 + "serde", 2868 + "serde_bytes", 2869 + ] 2870 + 2871 + [[package]] 2630 2872 name = "ipnet" 2631 2873 version = "2.11.0" 2632 2874 ··· 2671 2913 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 2672 2914 2673 2915 [[package]] 2916 + name = "jetstream-oxide" 2917 + version = "0.1.2" 2918 + source = "registry+https://github.com/rust-lang/crates.io-index" 2919 + checksum = "a267b4f381f1db945e9eb6b9ff0f3070950cc01521fef6265556b6a683e41e13" 2920 + dependencies = [ 2921 + "async-trait", 2922 + "atrium-api", 2923 + "chrono", 2924 + "flume", 2925 + "futures-util", 2926 + "log", 2927 + "serde", 2928 + "serde_json", 2929 + "thiserror 2.0.12", 2930 + "tokio", 2931 + "tokio-tungstenite 0.24.0", 2932 + "tokio-util", 2933 + "url", 2934 + "zstd", 2935 + ] 2936 + 2937 + [[package]] 2674 2938 name = "jni" 2675 2939 version = "0.21.1" 2676 2940 ··· 2855 3119 ] 2856 3120 2857 3121 [[package]] 3122 + name = "langtag" 3123 + version = "0.3.4" 3124 + source = "registry+https://github.com/rust-lang/crates.io-index" 3125 + checksum = "ed60c85f254d6ae8450cec15eedd921efbc4d1bdf6fcf6202b9a58b403f6f805" 3126 + dependencies = [ 3127 + "serde", 3128 + ] 3129 + 3130 + [[package]] 2858 3131 name = "language-tags" 2859 3132 version = "0.3.2" 2860 3133 ··· 3069 3342 checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 3070 3343 3071 3344 [[package]] 3345 + name = "lru" 3346 + version = "0.12.5" 3347 + source = "registry+https://github.com/rust-lang/crates.io-index" 3348 + checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 3349 + dependencies = [ 3350 + "hashbrown 0.15.3", 3351 + ] 3352 + 3353 + [[package]] 3072 3354 name = "lru-slab" 3073 3355 version = "0.1.2" 3074 3356 ··· 3185 3467 ] 3186 3468 3187 3469 [[package]] 3470 + name = "moka" 3471 + version = "0.12.11" 3472 + source = "registry+https://github.com/rust-lang/crates.io-index" 3473 + checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" 3474 + dependencies = [ 3475 + "async-lock", 3476 + "crossbeam-channel", 3477 + "crossbeam-epoch", 3478 + "crossbeam-utils", 3479 + "equivalent", 3480 + "event-listener", 3481 + "futures-util", 3482 + "parking_lot", 3483 + "portable-atomic", 3484 + "rustc_version", 3485 + "smallvec", 3486 + "tagptr", 3487 + "uuid", 3488 + ] 3489 + 3490 + [[package]] 3491 + name = "multer" 3492 + version = "2.1.0" 3493 + source = "registry+https://github.com/rust-lang/crates.io-index" 3494 + checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" 3495 + dependencies = [ 3496 + "bytes", 3497 + "encoding_rs", 3498 + "futures-util", 3499 + "http 0.2.12", 3500 + "httparse", 3501 + "log", 3502 + "memchr", 3503 + "mime", 3504 + "spin", 3505 + "version_check", 3506 + ] 3507 + 3508 + [[package]] 3509 + name = "multibase" 3510 + version = "0.9.1" 3511 + source = "registry+https://github.com/rust-lang/crates.io-index" 3512 + checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" 3513 + dependencies = [ 3514 + "base-x", 3515 + "data-encoding", 3516 + "data-encoding-macro", 3517 + ] 3518 + 3519 + [[package]] 3520 + name = "multihash" 3521 + version = "0.19.3" 3522 + source = "registry+https://github.com/rust-lang/crates.io-index" 3523 + checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" 3524 + dependencies = [ 3525 + "core2", 3526 + "serde", 3527 + "unsigned-varint", 3528 + ] 3529 + 3530 + [[package]] 3531 + name = "nanorand" 3532 + version = "0.7.0" 3533 + source = "registry+https://github.com/rust-lang/crates.io-index" 3534 + checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" 3535 + dependencies = [ 3536 + "getrandom 0.2.16", 3537 + ] 3538 + 3539 + [[package]] 3540 + name = "native-tls" 3541 + version = "0.2.14" 3542 + source = "registry+https://github.com/rust-lang/crates.io-index" 3543 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 3544 + dependencies = [ 3545 + "libc", 3546 + "log", 3547 + "openssl", 3548 + "openssl-probe", 3549 + "openssl-sys", 3550 + "schannel", 3551 + "security-framework 2.11.1", 3552 + "security-framework-sys", 3553 + "tempfile", 3554 + ] 3555 + 3556 + [[package]] 3188 3557 name = "nkeys" 3189 3558 version = "0.4.4" 3190 3559 ··· 3388 3757 checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 3389 3758 3390 3759 [[package]] 3760 + name = "openssl" 3761 + version = "0.10.73" 3762 + source = "registry+https://github.com/rust-lang/crates.io-index" 3763 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 3764 + dependencies = [ 3765 + "bitflags 2.9.1", 3766 + "cfg-if", 3767 + "foreign-types", 3768 + "libc", 3769 + "once_cell", 3770 + "openssl-macros", 3771 + "openssl-sys", 3772 + ] 3773 + 3774 + [[package]] 3775 + name = "openssl-macros" 3776 + version = "0.1.1" 3777 + source = "registry+https://github.com/rust-lang/crates.io-index" 3778 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 3779 + dependencies = [ 3780 + "proc-macro2", 3781 + "quote", 3782 + "syn 2.0.101", 3783 + ] 3784 + 3785 + [[package]] 3391 3786 name = "openssl-probe" 3392 3787 version = "0.1.6" 3393 3788 source = "registry+https://github.com/rust-lang/crates.io-index" 3394 3789 checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 3395 3790 3396 3791 [[package]] 3792 + name = "openssl-src" 3793 + version = "300.5.2+3.5.2" 3794 + source = "registry+https://github.com/rust-lang/crates.io-index" 3795 + checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" 3796 + dependencies = [ 3797 + "cc", 3798 + ] 3799 + 3800 + [[package]] 3801 + name = "openssl-sys" 3802 + version = "0.9.109" 3803 + source = "registry+https://github.com/rust-lang/crates.io-index" 3804 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 3805 + dependencies = [ 3806 + "cc", 3807 + "libc", 3808 + "openssl-src", 3809 + "pkg-config", 3810 + "vcpkg", 3811 + ] 3812 + 3813 + [[package]] 3397 3814 name = "option-ext" 3398 3815 version = "0.2.0" 3399 3816 ··· 4745 5162 source = "registry+https://github.com/rust-lang/crates.io-index" 4746 5163 checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 4747 5164 dependencies = [ 5165 + "async-compression", 4748 5166 "base64 0.22.1", 4749 5167 "bytes", 4750 5168 "futures-core", ··· 4754 5172 "http-body-util", 4755 5173 "hyper 1.6.0", 4756 5174 "hyper-rustls 0.27.6", 5175 + "hyper-tls", 4757 5176 "hyper-util", 4758 5177 "ipnet", 4759 5178 "js-sys", 4760 5179 "log", 4761 5180 "mime", 4762 5181 "mime_guess", 5182 + "native-tls", 4763 5183 "once_cell", 4764 5184 "percent-encoding", 4765 5185 "pin-project-lite", ··· 4772 5192 "serde_urlencoded", 4773 5193 "sync_wrapper", 4774 5194 "tokio", 5195 + "tokio-native-tls", 4775 5196 "tokio-rustls 0.26.2", 4776 5197 "tokio-util", 4777 5198 "tower", ··· 4866 5287 "serde_json", 4867 5288 "tokio", 4868 5289 "tokio-stream", 4869 - "tokio-tungstenite", 4870 - "tungstenite", 5290 + "tokio-tungstenite 0.26.2", 5291 + "tungstenite 0.26.2", 4871 5292 ] 4872 5293 4873 5294 [[package]] ··· 4902 5323 ] 4903 5324 4904 5325 [[package]] 5326 + name = "rocksky-feed" 5327 + version = "0.1.0" 5328 + dependencies = [ 5329 + "anyhow", 5330 + "atrium-api", 5331 + "atrium-xrpc-client", 5332 + "chrono", 5333 + "dotenv", 5334 + "duckdb", 5335 + "jetstream-oxide", 5336 + "owo-colors", 5337 + "reqwest", 5338 + "serde", 5339 + "serde_json", 5340 + "sqlx", 5341 + "tokio", 5342 + "tracing", 5343 + "warp", 5344 + ] 5345 + 5346 + [[package]] 4905 5347 name = "rocksky-googledrive" 4906 5348 version = "0.1.0" 4907 5349 ··· 4952 5394 "time", 4953 5395 "tokio", 4954 5396 "tokio-stream", 4955 - "tokio-tungstenite", 5397 + "tokio-tungstenite 0.26.2", 4956 5398 "tracing", 4957 - "tungstenite", 5399 + "tungstenite 0.26.2", 4958 5400 "url", 4959 5401 ] 4960 5402 ··· 5108 5550 5109 5551 5110 5552 5553 + "owo-colors", 5554 + "rocksky-analytics", 5555 + "rocksky-dropbox", 5556 + "rocksky-feed", 5557 + "rocksky-googledrive", 5558 + "rocksky-jetstream", 5559 + "rocksky-playlists", 5111 5560 5112 5561 5113 5562 ··· 5438 5887 5439 5888 5440 5889 5441 - 5442 - 5443 - 5444 - 5445 - 5446 - 5447 5890 "windows-sys 0.59.0", 5448 5891 ] 5449 5892 5450 5893 [[package]] 5894 + name = "scoped-tls" 5895 + version = "1.0.1" 5896 + source = "registry+https://github.com/rust-lang/crates.io-index" 5897 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 5898 + 5899 + [[package]] 5451 5900 name = "scopeguard" 5452 5901 version = "1.2.0" 5453 5902 ··· 5522 5971 5523 5972 [[package]] 5524 5973 name = "serde" 5525 - version = "1.0.219" 5974 + version = "1.0.227" 5526 5975 source = "registry+https://github.com/rust-lang/crates.io-index" 5527 - checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 5976 + checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" 5528 5977 dependencies = [ 5978 + "serde_core", 5529 5979 "serde_derive", 5530 5980 ] 5531 5981 5532 5982 [[package]] 5983 + name = "serde_bytes" 5984 + version = "0.11.19" 5985 + source = "registry+https://github.com/rust-lang/crates.io-index" 5986 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 5987 + dependencies = [ 5988 + "serde", 5989 + "serde_core", 5990 + ] 5991 + 5992 + [[package]] 5533 5993 name = "serde_cbor" 5534 5994 version = "0.11.2" 5535 5995 ··· 5540 6000 ] 5541 6001 5542 6002 [[package]] 6003 + name = "serde_core" 6004 + version = "1.0.227" 6005 + source = "registry+https://github.com/rust-lang/crates.io-index" 6006 + checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" 6007 + dependencies = [ 6008 + "serde_derive", 6009 + ] 6010 + 6011 + [[package]] 5543 6012 name = "serde_derive" 5544 - version = "1.0.219" 6013 + version = "1.0.227" 5545 6014 source = "registry+https://github.com/rust-lang/crates.io-index" 5546 - checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 6015 + checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" 5547 6016 dependencies = [ 5548 6017 "proc-macro2", 5549 6018 "quote", ··· 5551 6020 ] 5552 6021 5553 6022 [[package]] 6023 + name = "serde_html_form" 6024 + version = "0.2.8" 6025 + source = "registry+https://github.com/rust-lang/crates.io-index" 6026 + checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 6027 + dependencies = [ 6028 + "form_urlencoded", 6029 + "indexmap", 6030 + "itoa", 6031 + "ryu", 6032 + "serde_core", 6033 + ] 6034 + 6035 + [[package]] 5554 6036 name = "serde_json" 5555 - version = "1.0.140" 6037 + version = "1.0.145" 5556 6038 source = "registry+https://github.com/rust-lang/crates.io-index" 5557 - checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 6039 + checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 5558 6040 dependencies = [ 5559 6041 "itoa", 5560 6042 "memchr", 5561 6043 "ryu", 5562 6044 "serde", 6045 + "serde_core", 5563 6046 ] 5564 6047 5565 6048 [[package]] ··· 6403 6886 ] 6404 6887 6405 6888 [[package]] 6889 + name = "tagptr" 6890 + version = "0.2.0" 6891 + source = "registry+https://github.com/rust-lang/crates.io-index" 6892 + checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" 6893 + 6894 + [[package]] 6406 6895 name = "tap" 6407 6896 version = "1.0.1" 6408 6897 ··· 6576 7065 ] 6577 7066 6578 7067 [[package]] 7068 + name = "tokio-native-tls" 7069 + version = "0.3.1" 7070 + source = "registry+https://github.com/rust-lang/crates.io-index" 7071 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 7072 + dependencies = [ 7073 + "native-tls", 7074 + "tokio", 7075 + ] 7076 + 7077 + [[package]] 6579 7078 name = "tokio-postgres" 6580 7079 version = "0.7.13" 6581 7080 ··· 6635 7134 6636 7135 [[package]] 6637 7136 name = "tokio-tungstenite" 7137 + version = "0.21.0" 7138 + source = "registry+https://github.com/rust-lang/crates.io-index" 7139 + checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" 7140 + dependencies = [ 7141 + "futures-util", 7142 + "log", 7143 + "tokio", 7144 + "tungstenite 0.21.0", 7145 + ] 7146 + 7147 + [[package]] 7148 + name = "tokio-tungstenite" 7149 + version = "0.24.0" 7150 + source = "registry+https://github.com/rust-lang/crates.io-index" 7151 + checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" 7152 + dependencies = [ 7153 + "futures-util", 7154 + "log", 7155 + "native-tls", 7156 + "tokio", 7157 + "tokio-native-tls", 7158 + "tungstenite 0.24.0", 7159 + ] 7160 + 7161 + [[package]] 7162 + name = "tokio-tungstenite" 6638 7163 version = "0.26.2" 6639 7164 6640 7165 ··· 6645 7170 "rustls-pki-types", 6646 7171 "tokio", 6647 7172 "tokio-rustls 0.26.2", 6648 - "tungstenite", 7173 + "tungstenite 0.26.2", 6649 7174 "webpki-roots 0.26.11", 6650 7175 ] 6651 7176 ··· 6850 7375 ] 6851 7376 6852 7377 [[package]] 7378 + name = "trait-variant" 7379 + version = "0.1.2" 7380 + source = "registry+https://github.com/rust-lang/crates.io-index" 7381 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 7382 + dependencies = [ 7383 + "proc-macro2", 7384 + "quote", 7385 + "syn 2.0.101", 7386 + ] 7387 + 7388 + [[package]] 6853 7389 name = "trim-in-place" 6854 7390 version = "0.1.7" 6855 7391 ··· 6874 7410 6875 7411 [[package]] 6876 7412 name = "tungstenite" 7413 + version = "0.21.0" 7414 + source = "registry+https://github.com/rust-lang/crates.io-index" 7415 + checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" 7416 + dependencies = [ 7417 + "byteorder", 7418 + "bytes", 7419 + "data-encoding", 7420 + "http 1.3.1", 7421 + "httparse", 7422 + "log", 7423 + "rand 0.8.5", 7424 + "sha1", 7425 + "thiserror 1.0.69", 7426 + "url", 7427 + "utf-8", 7428 + ] 7429 + 7430 + [[package]] 7431 + name = "tungstenite" 7432 + version = "0.24.0" 7433 + source = "registry+https://github.com/rust-lang/crates.io-index" 7434 + checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" 7435 + dependencies = [ 7436 + "byteorder", 7437 + "bytes", 7438 + "data-encoding", 7439 + "http 1.3.1", 7440 + "httparse", 7441 + "log", 7442 + "native-tls", 7443 + "rand 0.8.5", 7444 + "sha1", 7445 + "thiserror 1.0.69", 7446 + "url", 7447 + "utf-8", 7448 + ] 7449 + 7450 + [[package]] 7451 + name = "tungstenite" 6877 7452 version = "0.26.2" 6878 7453 6879 7454 ··· 6980 7555 ] 6981 7556 6982 7557 [[package]] 7558 + name = "unsigned-varint" 7559 + version = "0.8.0" 7560 + source = "registry+https://github.com/rust-lang/crates.io-index" 7561 + checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 7562 + 7563 + [[package]] 6983 7564 name = "untrusted" 6984 7565 version = "0.9.0" 6985 7566 ··· 7069 7650 7070 7651 7071 7652 "try-lock", 7653 + ] 7654 + 7655 + [[package]] 7656 + name = "warp" 7657 + version = "0.3.7" 7658 + source = "registry+https://github.com/rust-lang/crates.io-index" 7659 + checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" 7660 + dependencies = [ 7661 + "bytes", 7662 + "futures-channel", 7663 + "futures-util", 7664 + "headers", 7665 + "http 0.2.12", 7666 + "hyper 0.14.32", 7667 + "log", 7668 + "mime", 7669 + "mime_guess", 7670 + "multer", 7671 + "percent-encoding", 7672 + "pin-project", 7673 + "scoped-tls", 7674 + "serde", 7675 + "serde_json", 7676 + "serde_urlencoded", 7677 + "tokio", 7678 + "tokio-tungstenite 0.21.0", 7679 + "tokio-util", 7680 + "tower-service", 7681 + "tracing", 7072 7682 ] 7073 7683 7074 7684 [[package]]
+35
crates/feed/Cargo.toml
··· 1 + [package] 2 + name = "rocksky-feed" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + warp = "0.3.7" 11 + owo-colors = "4.1.0" 12 + anyhow = "1.0.100" 13 + atrium-api = "0.25.5" 14 + atrium-xrpc-client = "0.5.14" 15 + chrono = { version = "= 0.4.39", features = ["serde"] } 16 + duckdb = { version = "1.2.0", features = ["chrono"] } 17 + jetstream-oxide = "=0.1.2" 18 + reqwest = { version = "0.12.12", features = [ 19 + "rustls-tls", 20 + "json", 21 + "multipart", 22 + ], default-features = false } 23 + serde = "1.0.227" 24 + serde_json = "1.0.145" 25 + tokio = { version = "1.43.0", features = ["full"] } 26 + tracing = "0.1.41" 27 + dotenv = "0.15.0" 28 + sqlx = { version = "0.8.3", features = [ 29 + "runtime-tokio", 30 + "tls-rustls", 31 + "postgres", 32 + "chrono", 33 + "derive", 34 + "macros", 35 + ] }
+35
crates/feed/src/config.rs
··· 1 + use std::env; 2 + 3 + use dotenv::dotenv; 4 + 5 + #[derive(Debug, Clone)] 6 + /// Configuration values for a Feed service 7 + pub struct Config { 8 + /// Your account's decentralized identifier (DID) 9 + /// A DID is a persistent, long-term identifier for every account. Usually look like did:plc:ewvi7nxzyoun6zhxrhs64oiz. 10 + pub publisher_did: String, 11 + /// The host name for your feed generator. 12 + /// 13 + /// For example: if github were to host a feed generator service at their domain they would set this value to `github.com` 14 + /// 15 + /// You can develop your feed locally without setting this to a real value. However, when publishing, this value must be a domain that: 16 + /// - Points to your service. 17 + /// - Is secured with SSL (HTTPS). 18 + /// - Is accessible on the public internet. 19 + pub feed_generator_hostname: String, 20 + } 21 + 22 + impl Config { 23 + /// Loads the config from a local .env file containing these variables 24 + /// PUBLISHER_DID 25 + /// FEED_GENERATOR_HOSTNAME 26 + pub fn load_env_config() -> Self { 27 + dotenv().expect("Missing .env"); 28 + Config { 29 + publisher_did: env::var("PUBLISHER_DID") 30 + .expect(".env file is missing an entry for PUBLISHER_DID"), 31 + feed_generator_hostname: env::var("FEED_GENERATOR_HOSTNAME") 32 + .expect(".env file is missing an entry for FEED_GENERATOR_HOSTNAME"), 33 + } 34 + } 35 + }
+129
crates/feed/src/feed.rs
··· 1 + use crate::config::Config; 2 + use crate::types::{DidDocument, FeedSkeletonParameters, Service}; 3 + use crate::{feed_handler::FeedHandler, types::FeedSkeletonQuery}; 4 + use std::fmt::Debug; 5 + use std::net::SocketAddr; 6 + use warp::Filter; 7 + 8 + /// A `Feed` stores a `FeedHandler`, handles feed server endpoints & connects to the Firehose using the `start` methods. 9 + pub trait Feed<Handler: FeedHandler + Clone + Send + Sync + 'static> { 10 + fn handler(&mut self) -> Handler; 11 + /// Starts the feed generator server & connects to the firehose. 12 + /// 13 + /// 14 + /// - name: The identifying name of your feed. This value is used in the feed URL & when identifying which feed to *unpublish*. This is a separate value from the display name. 15 + /// - address: The address to bind the server to 16 + /// 17 + /// # Panics 18 + /// 19 + /// Panics if unable to bind to the provided address. 20 + fn start( 21 + &mut self, 22 + name: impl AsRef<str>, 23 + address: impl Into<SocketAddr> + Debug + Clone + Send, 24 + ) -> impl std::future::Future<Output = ()> + Send { 25 + self.start_with_config(name, Config::load_env_config(), address) 26 + } 27 + 28 + /// Starts the feed generator server & connects to the firehose. 29 + /// 30 + /// - name: The identifying name of your feed. This value is used in the feed URL & when identifying which feed to *unpublish*. This is a separate value from the display name. 31 + /// - config: Configuration values, see `Config` 32 + /// - address: The address to bind the server to 33 + /// 34 + /// # Panics 35 + /// 36 + /// Panics if unable to bind to the provided address. 37 + fn start_with_config( 38 + &mut self, 39 + name: impl AsRef<str>, 40 + config: Config, 41 + address: impl Into<SocketAddr> + Debug + Clone + Send, 42 + ) -> impl std::future::Future<Output = ()> + Send { 43 + let handler = self.handler(); 44 + let address = address.clone(); 45 + let feed_name = name.as_ref().to_string(); 46 + 47 + async move { 48 + let config = config; 49 + 50 + let did_config = config.clone(); 51 + let did_json = warp::path(".well-known") 52 + .and(warp::path("did.json")) 53 + .and(warp::get()) 54 + .and_then(move || did_json(did_config.clone())); 55 + 56 + let describe_feed_generator = warp::path("xrpc") 57 + .and(warp::path("app.rocksky.feed.describeFeedGenerator")) 58 + .and(warp::get()) 59 + .and_then(move || describe_feed_generator(feed_name.clone())); 60 + 61 + let get_feed_handler = handler.clone(); 62 + let get_feed_skeleton = warp::path("xrpc") 63 + .and(warp::path("app.rocksky.feed.getFeedSkeleton")) 64 + .and(warp::get()) 65 + .and(warp::query::<FeedSkeletonParameters>()) 66 + .and_then(move |query: FeedSkeletonParameters| { 67 + get_feed_skeleton::<Handler>(query.into(), get_feed_handler.clone()) 68 + }); 69 + 70 + let api = did_json.or(describe_feed_generator).or(get_feed_skeleton); 71 + 72 + tracing::info!("Serving feed on {}", format!("{:?}", address)); 73 + 74 + let routes = api.with(warp::log::custom(|info| { 75 + let method = info.method(); 76 + let path = info.path(); 77 + let status = info.status(); 78 + let elapsed = info.elapsed().as_millis(); 79 + 80 + if status.is_success() { 81 + tracing::info!( 82 + "Method: {}, Path: {}, Status: {}, Elapsed Time: {}ms", 83 + method, 84 + path, 85 + status, 86 + elapsed 87 + ); 88 + } else { 89 + tracing::error!( 90 + "Method: {}, Path: {}, Status: {}, Elapsed Time: {}ms", 91 + method, 92 + path, 93 + status, 94 + elapsed, 95 + ); 96 + } 97 + })); 98 + let feed_server = warp::serve(routes); 99 + let firehose_listener = tokio::spawn(async move {}); 100 + 101 + tokio::join!(feed_server.run(address), firehose_listener) 102 + .1 103 + .expect("Couldn't await tasks"); 104 + } 105 + } 106 + } 107 + 108 + async fn did_json(config: Config) -> Result<impl warp::Reply, warp::Rejection> { 109 + Ok(warp::reply::json(&DidDocument { 110 + context: vec!["https://www.w3.org/ns/did/v1".to_owned()], 111 + id: format!("did:web:{}", config.feed_generator_hostname), 112 + service: vec![Service { 113 + id: "#rocksky_fg".to_owned(), 114 + type_: "RockskyFeedGenerator".to_owned(), 115 + service_endpoint: format!("https://{}", config.feed_generator_hostname), 116 + }], 117 + })) 118 + } 119 + 120 + async fn describe_feed_generator(feed_name: String) -> Result<impl warp::Reply, warp::Rejection> { 121 + Ok(warp::reply::json(&serde_json::json!({}))) 122 + } 123 + 124 + async fn get_feed_skeleton<Handler: FeedHandler>( 125 + query: FeedSkeletonQuery, 126 + handler: Handler, 127 + ) -> Result<impl warp::Reply, warp::Rejection> { 128 + Ok(warp::reply::json(&serde_json::json!({}))) 129 + }
+10
crates/feed/src/feed_handler.rs
··· 1 + use crate::types::{FeedResult, Request, Scrobble, Uri}; 2 + 3 + /// A feed handler is responsible for 4 + /// - Storing and managing firehose input. 5 + /// - Serving responses to feed requests with `serve_feed` 6 + pub trait FeedHandler { 7 + fn insert_scrobble(&self, scrobble: Scrobble) -> impl std::future::Future<Output = ()> + Send; 8 + fn delete_scrobble(&self, uri: Uri) -> impl std::future::Future<Output = ()> + Send; 9 + fn serve_feed(&self, request: Request) -> impl std::future::Future<Output = FeedResult> + Send; 10 + }
+57
crates/feed/src/lib.rs
··· 1 + use std::{env, net::SocketAddr, sync::Arc}; 2 + 3 + use tokio::sync::Mutex; 4 + 5 + use crate::{ 6 + feed::Feed, 7 + feed_handler::FeedHandler, 8 + types::{FeedResult, Scrobble}, 9 + }; 10 + 11 + pub mod config; 12 + pub mod feed; 13 + pub mod feed_handler; 14 + pub mod types; 15 + 16 + pub struct RecentlyPlayedFeed { 17 + handler: RecentlyPlayedFeedHandler, 18 + } 19 + 20 + impl Feed<RecentlyPlayedFeedHandler> for RecentlyPlayedFeed { 21 + fn handler(&mut self) -> RecentlyPlayedFeedHandler { 22 + self.handler.clone() 23 + } 24 + } 25 + 26 + #[derive(Clone)] 27 + pub struct RecentlyPlayedFeedHandler { 28 + pub scrobbles: Arc<Mutex<Vec<Scrobble>>>, 29 + } 30 + 31 + impl FeedHandler for RecentlyPlayedFeedHandler { 32 + async fn insert_scrobble(&self, scrobble: Scrobble) { 33 + todo!() 34 + } 35 + 36 + async fn delete_scrobble(&self, uri: types::Uri) { 37 + todo!() 38 + } 39 + 40 + async fn serve_feed(&self, request: types::Request) -> FeedResult { 41 + todo!() 42 + } 43 + } 44 + 45 + pub async fn run() { 46 + let mut feed = RecentlyPlayedFeed { 47 + handler: RecentlyPlayedFeedHandler { 48 + scrobbles: Arc::new(Mutex::new(Vec::new())), 49 + }, 50 + }; 51 + let host = env::var("FEED_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); 52 + let port = env::var("FEED_PORT").unwrap_or_else(|_| "7885".to_string()); 53 + let addr_str = format!("{}:{}", host, port); 54 + let addr: SocketAddr = addr_str.parse().expect("Invalid address format"); 55 + 56 + feed.start("RecentlyPlayed", addr).await; 57 + }
+54
crates/feed/src/types.rs
··· 1 + #[derive(Debug, Clone)] 2 + pub struct Request { 3 + pub cursor: Option<String>, 4 + pub feed: String, 5 + pub limit: Option<u8>, 6 + } 7 + 8 + #[derive(Debug, Clone)] 9 + pub struct Cid(pub String); 10 + 11 + #[derive(Debug, Clone)] 12 + pub struct Did(pub String); 13 + 14 + #[derive(Debug, Clone, Hash, PartialEq, Eq)] 15 + pub struct Uri(pub String); 16 + 17 + #[derive(Debug, Clone)] 18 + pub struct FeedResult { 19 + pub cursor: Option<String>, 20 + pub feed: Vec<Uri>, 21 + } 22 + 23 + pub struct FeedSkeletonQuery {} 24 + 25 + #[derive(Deserialize)] 26 + pub struct FeedSkeletonParameters {} 27 + 28 + impl Into<FeedSkeletonQuery> for FeedSkeletonParameters { 29 + fn into(self) -> FeedSkeletonQuery { 30 + FeedSkeletonQuery {} 31 + } 32 + } 33 + 34 + #[derive(Debug, Clone)] 35 + pub struct Scrobble {} 36 + 37 + use serde::{Deserialize, Serialize}; 38 + 39 + #[derive(Serialize)] 40 + pub(crate) struct DidDocument { 41 + #[serde(rename = "@context")] 42 + pub(crate) context: Vec<String>, 43 + pub(crate) id: String, 44 + pub(crate) service: Vec<Service>, 45 + } 46 + 47 + #[derive(Serialize)] 48 + pub(crate) struct Service { 49 + pub(crate) id: String, 50 + #[serde(rename = "type")] 51 + pub(crate) type_: String, 52 + #[serde(rename = "serviceEndpoint")] 53 + pub(crate) service_endpoint: String, 54 + }
+1
crates/rockskyd/Cargo.toml
··· 20 20 rocksky-spotify = { path = "../spotify" } 21 21 rocksky-tracklist = { path = "../tracklist" } 22 22 rocksky-webscrobbler = { path = "../webscrobbler" } 23 + rocksky-feed = { path = "../feed" } 23 24 tracing = "0.1.41" 24 25 tracing-subscriber = "0.3.20" 25 26 tracing-log = "0.2.0"
+6
crates/rockskyd/src/cmd/feed.rs
··· 1 + use anyhow::Error; 2 + 3 + pub async fn serve() -> Result<(), Error> { 4 + rocksky_feed::run().await; 5 + Ok(()) 6 + }
+1
crates/rockskyd/src/cmd/mod.rs
··· 1 1 pub mod analytics; 2 2 pub mod dropbox; 3 + pub mod feed; 3 4 pub mod googledrive; 4 5 pub mod jetstream; 5 6 pub mod playlist;
+9
crates/rockskyd/src/main.rs
··· 32 32 .subcommand(Command::new("spotify").about("Start Spotify Listener Service")) 33 33 .subcommand(Command::new("tracklist").about("Start User Current Track Queue Service")) 34 34 .subcommand(Command::new("webscrobbler").about("Start Webscrobbler API")) 35 + .subcommand( 36 + Command::new("feed") 37 + .about("Feed related commands") 38 + .subcommand(Command::new("serve").about("Serve the Rocksky Feed API")), 39 + ) 35 40 } 36 41 37 42 #[tokio::main] ··· 85 90 Some(("webscrobbler", _)) => { 86 91 cmd::webscrobbler::start_webscrobbler_service().await?; 87 92 } 93 + Some(("feed", sub_m)) => match sub_m.subcommand() { 94 + Some(("serve", _)) => cmd::feed::serve().await?, 95 + _ => println!("Unknown feed command"), 96 + }, 88 97 _ => { 89 98 println!("No valid subcommand was used. Use --help to see available commands."); 90 99 }

History

2 rounds 0 comments
sign up or login to add to the discussion
4 commits
expand
feat(feed): implement feed service with configuration and handler
feat(feed): add feed command with serve subcommand and integrate feed module
fix: update feed and lib modules to return Result types for better error handling
feat: add new dependencies for moka, multer, multibase, multihash, nanorand, and native-tls
expand 0 comments
closed without merging
2 commits
expand
feat(feed): implement feed service with configuration and handler
feat(feed): add feed command with serve subcommand and integrate feed module
expand 0 comments