A decentralized music tracking and discovery platform built on AT Protocol 🎵
0
fork

Configure Feed

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

Add collections script and did:web support

+110 -10
+1
apps/api/package.json
··· 16 16 "genres": "tsx ./src/scripts/genres.ts", 17 17 "spotify": "tsx ./src/scripts/spotify.ts", 18 18 "exp": "tsx ./src/scripts/exp.ts", 19 + "collections": "tsx ./src/scripts/collections.ts", 19 20 "pkl:eval": "pkl eval -f json", 20 21 "pkl:gen": "tsx ./scripts/pkl.ts", 21 22 "dev:xrpc": "tsx --watch ./src/server.ts",
+88
apps/api/src/scripts/collections.ts
··· 1 + import { AtpAgent } from "@atproto/api"; 2 + import { Record } from "@atproto/api/dist/client/types/com/atproto/repo/listRecords"; 3 + import { consola } from "consola"; 4 + import { ctx } from "context"; 5 + import extractPdsFromDid from "lib/extractPdsFromDid"; 6 + import { chalk } from "zx/core"; 7 + 8 + const args = process.argv.slice(2); 9 + 10 + if (args.length === 0) { 11 + consola.error("Please provide user identifier (handle or DID)."); 12 + console.log(`Usage: ${chalk.cyan("npm run collections -- <handle|did>")}`); 13 + process.exit(1); 14 + } 15 + 16 + let did: string = args[0]; 17 + 18 + if (!did.startsWith("did:")) { 19 + did = await ctx.baseIdResolver.handle.resolve(did); 20 + } 21 + 22 + async function getAtpAgent(did: string): Promise<AtpAgent> { 23 + const serviceEndpoint = await extractPdsFromDid(did); 24 + 25 + consola.info(`Using service endpoint: ${chalk.cyan(serviceEndpoint)}`); 26 + 27 + return new AtpAgent({ service: serviceEndpoint }); 28 + } 29 + 30 + async function getScrobbleRecords(agent: AtpAgent, did: string) { 31 + const res = await agent.com.atproto.repo.listRecords({ 32 + repo: did, 33 + collection: "app.rocksky.scrobble", 34 + limit: 100, 35 + }); 36 + 37 + return res.data.records; 38 + } 39 + 40 + async function getSongRecords(agent: AtpAgent, did: string) { 41 + const res = await agent.com.atproto.repo.listRecords({ 42 + repo: did, 43 + collection: "app.rocksky.song", 44 + limit: 100, 45 + }); 46 + 47 + return res.data.records; 48 + } 49 + 50 + async function getArtistRecords(agent: AtpAgent, did: string) { 51 + const res = await agent.com.atproto.repo.listRecords({ 52 + repo: did, 53 + collection: "app.rocksky.artist", 54 + limit: 100, 55 + }); 56 + 57 + return res.data.records; 58 + } 59 + 60 + async function getAlbumRecords(agent: AtpAgent, did: string) { 61 + const res = await agent.com.atproto.repo.listRecords({ 62 + repo: did, 63 + collection: "app.rocksky.album", 64 + limit: 100, 65 + }); 66 + 67 + return res.data.records; 68 + } 69 + 70 + async function insertScrobbles(scrobbles: Record[]) {} 71 + 72 + async function main() { 73 + const agent = await getAtpAgent(did); 74 + const scrobbles = await getScrobbleRecords(agent, did); 75 + const songs = await getSongRecords(agent, did); 76 + const artists = await getArtistRecords(agent, did); 77 + const albums = await getAlbumRecords(agent, did); 78 + console.log(scrobbles); 79 + console.log(songs); 80 + console.log(artists); 81 + console.log(albums); 82 + 83 + consola.success(`${chalk.cyan(args[0])} Collections fetched successfully!`); 84 + 85 + process.exit(0); 86 + } 87 + 88 + await main();
+21 -10
crates/jetstream/src/profile.rs
··· 4 4 5 5 pub async fn did_to_profile(did: &str) -> Result<Profile, Error> { 6 6 let client = reqwest::Client::new(); 7 + let mut url = format!("https://plc.directory/{}", did); 8 + 9 + if did.starts_with("did:web:") { 10 + let domain = did 11 + .trim_start_matches("did:web:") 12 + .split(':') 13 + .next() 14 + .unwrap_or(""); 15 + url = format!("https://{}/.well-known/did.json", domain); 16 + } 17 + 7 18 let response = client 8 - .get(format!("https://plc.directory/{}", did)) 19 + .get(url) 9 20 .header("Accept", "application/json") 10 21 .send() 11 22 .await? ··· 59 70 .map(|s| s == "tsiry-sandratraina.com") 60 71 .unwrap_or(false)); 61 72 62 - let did = "did:plc:fgvx5xqinqoqgpfhito5er3s"; 73 + let did = "did:plc:d5jvs7uo4z6lw63zzreukgt4"; 63 74 let profile = did_to_profile(did).await?; 64 - 65 75 assert_eq!(profile.r#type, "app.bsky.actor.profile"); 66 - assert!(profile 67 - .display_name 68 - .map(|s| s.starts_with("Lixtrix")) 69 - .unwrap_or(false)); 70 - assert!(profile.handle.map(|s| s == "lixtrix.art").unwrap_or(false)); 71 76 72 - let did = "did:plc:d5jvs7uo4z6lw63zzreukgt4"; 77 + let did = "did:plc:gwxwdfmun3aqaiu5mx7nnyof"; 73 78 let profile = did_to_profile(did).await?; 74 79 assert_eq!(profile.r#type, "app.bsky.actor.profile"); 75 80 76 - let did = "did:plc:gwxwdfmun3aqaiu5mx7nnyof"; 81 + let did = "did:web:jb.waf.moe"; 77 82 let profile = did_to_profile(did).await?; 83 + 78 84 assert_eq!(profile.r#type, "app.bsky.actor.profile"); 85 + assert!(profile 86 + .display_name 87 + .map(|s| s.starts_with("jb")) 88 + .unwrap_or(false)); 89 + assert!(profile.handle.map(|s| s == "jbc.lol").unwrap_or(false)); 79 90 80 91 Ok(()) 81 92 }