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

Remove getNowPlayings endpoint and schema

+2 -546
-11
apps/api/bruno/rocksky/feed/Get Now Playings.bru
··· 1 - meta { 2 - name: Get Now Playings 3 - type: http 4 - seq: 1 5 - } 6 - 7 - get { 8 - url: {{url}}/xrpc/app.rocksky.feed.getNowPlayings 9 - body: none 10 - auth: inherit 11 - }
-69
apps/api/lexicons/feed/defs.json
··· 32 32 } 33 33 } 34 34 }, 35 - "nowPlayingView": { 36 - "type": "object", 37 - "properties": { 38 - "album": { 39 - "type": "string" 40 - }, 41 - "albumArt": { 42 - "type": "string", 43 - "format": "uri" 44 - }, 45 - "albumArtist": { 46 - "type": "string" 47 - }, 48 - "albumUri": { 49 - "type": "string", 50 - "format": "at-uri" 51 - }, 52 - "artist": { 53 - "type": "string" 54 - }, 55 - "artistUri": { 56 - "type": "string", 57 - "format": "at-uri" 58 - }, 59 - "avatar": { 60 - "type": "string", 61 - "format": "uri" 62 - }, 63 - "createdAt": { 64 - "type": "string" 65 - }, 66 - "did": { 67 - "type": "string", 68 - "format": "at-identifier" 69 - }, 70 - "handle": { 71 - "type": "string" 72 - }, 73 - "id": { 74 - "type": "string" 75 - }, 76 - "title": { 77 - "type": "string" 78 - }, 79 - "trackId": { 80 - "type": "string" 81 - }, 82 - "trackUri": { 83 - "type": "string", 84 - "format": "at-uri" 85 - }, 86 - "uri": { 87 - "type": "string", 88 - "format": "at-uri" 89 - } 90 - } 91 - }, 92 - "nowPlayingsView": { 93 - "type": "object", 94 - "properties": { 95 - "nowPlayings": { 96 - "type": "array", 97 - "items": { 98 - "type": "ref", 99 - "ref": "app.rocksky.feed.defs#nowPlayingView" 100 - } 101 - } 102 - } 103 - }, 104 35 "storyView": { 105 36 "type": "object", 106 37 "properties": {
-27
apps/api/lexicons/feed/getNowPlayings.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "app.rocksky.feed.getNowPlayings", 4 - "defs": { 5 - "main": { 6 - "type": "query", 7 - "description": "Get all currently playing tracks by users", 8 - "parameters": { 9 - "type": "params", 10 - "properties": { 11 - "size": { 12 - "type": "integer", 13 - "description": "The maximum number of now playing tracks to return.", 14 - "minimum": 1 15 - } 16 - } 17 - }, 18 - "output": { 19 - "encoding": "application/json", 20 - "schema": { 21 - "type": "ref", 22 - "ref": "app.rocksky.feed.defs#nowPlayingsView" 23 - } 24 - } 25 - } 26 - } 27 - }
-69
apps/api/pkl/defs/feed/defs.pkl
··· 34 34 } 35 35 } 36 36 } 37 - ["nowPlayingView"] = new ObjectType { 38 - type = "object" 39 - properties { 40 - ["album"] = new StringType { 41 - type = "string" 42 - } 43 - ["albumArt"] = new StringType { 44 - type = "string" 45 - format = "uri" 46 - } 47 - ["albumArtist"] = new StringType { 48 - type = "string" 49 - } 50 - ["albumUri"] = new StringType { 51 - type = "string" 52 - format = "at-uri" 53 - } 54 - ["artist"] = new StringType { 55 - type = "string" 56 - } 57 - ["artistUri"] = new StringType { 58 - type = "string" 59 - format = "at-uri" 60 - } 61 - ["avatar"] = new StringType { 62 - type = "string" 63 - format = "uri" 64 - } 65 - ["createdAt"] = new StringType { 66 - type = "string" 67 - } 68 - ["did"] = new StringType { 69 - type = "string" 70 - format = "at-identifier" 71 - } 72 - ["handle"] = new StringType { 73 - type = "string" 74 - } 75 - ["id"] = new StringType { 76 - type = "string" 77 - } 78 - ["title"] = new StringType { 79 - type = "string" 80 - } 81 - ["trackId"] = new StringType { 82 - type = "string" 83 - } 84 - ["trackUri"] = new StringType { 85 - type = "string" 86 - format = "at-uri" 87 - } 88 - ["uri"] = new StringType { 89 - type = "string" 90 - format = "at-uri" 91 - } 92 - } 93 - } 94 - ["nowPlayingsView"] = new ObjectType { 95 - type = "object" 96 - properties { 97 - ["nowPlayings"] = new Array { 98 - type = "array" 99 - items = new Ref { 100 - type = "ref" 101 - ref = "app.rocksky.feed.defs#nowPlayingView" 102 - } 103 - } 104 - } 105 - } 106 37 ["storyView"] = new ObjectType { 107 38 type = "object" 108 39 properties {
-27
apps/api/pkl/defs/feed/getNowPlayings.pkl
··· 1 - amends "../../schema/lexicon.pkl" 2 - 3 - lexicon = 1 4 - id = "app.rocksky.feed.getNowPlayings" 5 - defs = new Mapping<String, Query> { 6 - ["main"] { 7 - type = "query" 8 - description = "Get all currently playing tracks by users" 9 - parameters { 10 - type = "params" 11 - properties { 12 - ["size"] = new IntegerType { 13 - type = "integer" 14 - description = "The maximum number of now playing tracks to return." 15 - minimum = 1 16 - } 17 - } 18 - } 19 - output { 20 - encoding = "application/json" 21 - schema = new Ref { 22 - type = "ref" 23 - ref = "app.rocksky.feed.defs#nowPlayingsView" 24 - } 25 - } 26 - } 27 - }
-12
apps/api/src/lexicon/index.ts
··· 47 47 import type * as AppRockskyFeedGetFeedGenerator from "./types/app/rocksky/feed/getFeedGenerator"; 48 48 import type * as AppRockskyFeedGetFeedGenerators from "./types/app/rocksky/feed/getFeedGenerators"; 49 49 import type * as AppRockskyFeedGetFeedSkeleton from "./types/app/rocksky/feed/getFeedSkeleton"; 50 - import type * as AppRockskyFeedGetNowPlayings from "./types/app/rocksky/feed/getNowPlayings"; 51 50 import type * as AppRockskyFeedGetStories from "./types/app/rocksky/feed/getStories"; 52 51 import type * as AppRockskyFeedSearch from "./types/app/rocksky/feed/search"; 53 52 import type * as AppRockskyGoogledriveDownloadFile from "./types/app/rocksky/googledrive/downloadFile"; ··· 695 694 >, 696 695 ) { 697 696 const nsid = "app.rocksky.feed.getFeedSkeleton"; // @ts-ignore 698 - return this._server.xrpc.method(nsid, cfg); 699 - } 700 - 701 - getNowPlayings<AV extends AuthVerifier>( 702 - cfg: ConfigOf< 703 - AV, 704 - AppRockskyFeedGetNowPlayings.Handler<ExtractAuth<AV>>, 705 - AppRockskyFeedGetNowPlayings.HandlerReqCtx<ExtractAuth<AV>> 706 - >, 707 - ) { 708 - const nsid = "app.rocksky.feed.getNowPlayings"; // @ts-ignore 709 697 return this._server.xrpc.method(nsid, cfg); 710 698 } 711 699
-98
apps/api/src/lexicon/lexicons.ts
··· 2548 2548 }, 2549 2549 }, 2550 2550 }, 2551 - nowPlayingView: { 2552 - type: "object", 2553 - properties: { 2554 - album: { 2555 - type: "string", 2556 - }, 2557 - albumArt: { 2558 - type: "string", 2559 - format: "uri", 2560 - }, 2561 - albumArtist: { 2562 - type: "string", 2563 - }, 2564 - albumUri: { 2565 - type: "string", 2566 - format: "at-uri", 2567 - }, 2568 - artist: { 2569 - type: "string", 2570 - }, 2571 - artistUri: { 2572 - type: "string", 2573 - format: "at-uri", 2574 - }, 2575 - avatar: { 2576 - type: "string", 2577 - format: "uri", 2578 - }, 2579 - createdAt: { 2580 - type: "string", 2581 - }, 2582 - did: { 2583 - type: "string", 2584 - format: "at-identifier", 2585 - }, 2586 - handle: { 2587 - type: "string", 2588 - }, 2589 - id: { 2590 - type: "string", 2591 - }, 2592 - title: { 2593 - type: "string", 2594 - }, 2595 - trackId: { 2596 - type: "string", 2597 - }, 2598 - trackUri: { 2599 - type: "string", 2600 - format: "at-uri", 2601 - }, 2602 - uri: { 2603 - type: "string", 2604 - format: "at-uri", 2605 - }, 2606 - }, 2607 - }, 2608 - nowPlayingsView: { 2609 - type: "object", 2610 - properties: { 2611 - nowPlayings: { 2612 - type: "array", 2613 - items: { 2614 - type: "ref", 2615 - ref: "lex:app.rocksky.feed.defs#nowPlayingView", 2616 - }, 2617 - }, 2618 - }, 2619 - }, 2620 2551 storyView: { 2621 2552 type: "object", 2622 2553 properties: { ··· 2985 2916 "The pagination cursor for the next set of results.", 2986 2917 }, 2987 2918 }, 2988 - }, 2989 - }, 2990 - }, 2991 - }, 2992 - }, 2993 - AppRockskyFeedGetNowPlayings: { 2994 - lexicon: 1, 2995 - id: "app.rocksky.feed.getNowPlayings", 2996 - defs: { 2997 - main: { 2998 - type: "query", 2999 - description: "Get all currently playing tracks by users", 3000 - parameters: { 3001 - type: "params", 3002 - properties: { 3003 - size: { 3004 - type: "integer", 3005 - description: 3006 - "The maximum number of now playing tracks to return.", 3007 - minimum: 1, 3008 - }, 3009 - }, 3010 - }, 3011 - output: { 3012 - encoding: "application/json", 3013 - schema: { 3014 - type: "ref", 3015 - ref: "lex:app.rocksky.feed.defs#nowPlayingsView", 3016 2919 }, 3017 2920 }, 3018 2921 }, ··· 6282 6185 AppRockskyFeedGetFeedGenerator: "app.rocksky.feed.getFeedGenerator", 6283 6186 AppRockskyFeedGetFeedGenerators: "app.rocksky.feed.getFeedGenerators", 6284 6187 AppRockskyFeedGetFeedSkeleton: "app.rocksky.feed.getFeedSkeleton", 6285 - AppRockskyFeedGetNowPlayings: "app.rocksky.feed.getNowPlayings", 6286 6188 AppRockskyFeedGetStories: "app.rocksky.feed.getStories", 6287 6189 AppRockskyFeedSearch: "app.rocksky.feed.search", 6288 6190 AppRockskyGoogledriveDefs: "app.rocksky.googledrive.defs",
-48
apps/api/src/lexicon/types/app/rocksky/feed/defs.ts
··· 40 40 return lexicons.validate("app.rocksky.feed.defs#searchResultsView", v); 41 41 } 42 42 43 - export interface NowPlayingView { 44 - album?: string; 45 - albumArt?: string; 46 - albumArtist?: string; 47 - albumUri?: string; 48 - artist?: string; 49 - artistUri?: string; 50 - avatar?: string; 51 - createdAt?: string; 52 - did?: string; 53 - handle?: string; 54 - id?: string; 55 - title?: string; 56 - trackId?: string; 57 - trackUri?: string; 58 - uri?: string; 59 - [k: string]: unknown; 60 - } 61 - 62 - export function isNowPlayingView(v: unknown): v is NowPlayingView { 63 - return ( 64 - isObj(v) && 65 - hasProp(v, "$type") && 66 - v.$type === "app.rocksky.feed.defs#nowPlayingView" 67 - ); 68 - } 69 - 70 - export function validateNowPlayingView(v: unknown): ValidationResult { 71 - return lexicons.validate("app.rocksky.feed.defs#nowPlayingView", v); 72 - } 73 - 74 - export interface NowPlayingsView { 75 - nowPlayings?: NowPlayingView[]; 76 - [k: string]: unknown; 77 - } 78 - 79 - export function isNowPlayingsView(v: unknown): v is NowPlayingsView { 80 - return ( 81 - isObj(v) && 82 - hasProp(v, "$type") && 83 - v.$type === "app.rocksky.feed.defs#nowPlayingsView" 84 - ); 85 - } 86 - 87 - export function validateNowPlayingsView(v: unknown): ValidationResult { 88 - return lexicons.validate("app.rocksky.feed.defs#nowPlayingsView", v); 89 - } 90 - 91 43 export interface StoryView { 92 44 album?: string; 93 45 albumArt?: string;
-43
apps/api/src/lexicon/types/app/rocksky/feed/getNowPlayings.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import type express from "express"; 5 - import { ValidationResult, BlobRef } from "@atproto/lexicon"; 6 - import { lexicons } from "../../../../lexicons"; 7 - import { isObj, hasProp } from "../../../../util"; 8 - import { CID } from "multiformats/cid"; 9 - import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 - import type * as AppRockskyFeedDefs from "./defs"; 11 - 12 - export interface QueryParams { 13 - /** The maximum number of now playing tracks to return. */ 14 - size?: number; 15 - } 16 - 17 - export type InputSchema = undefined; 18 - export type OutputSchema = AppRockskyFeedDefs.NowPlayingsView; 19 - export type HandlerInput = undefined; 20 - 21 - export interface HandlerSuccess { 22 - encoding: "application/json"; 23 - body: OutputSchema; 24 - headers?: { [key: string]: string }; 25 - } 26 - 27 - export interface HandlerError { 28 - status: number; 29 - message?: string; 30 - } 31 - 32 - export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough; 33 - export type HandlerReqCtx<HA extends HandlerAuth = never> = { 34 - auth: HA; 35 - params: QueryParams; 36 - input: HandlerInput; 37 - req: express.Request; 38 - res: express.Response; 39 - resetRouteRateLimits: () => Promise<void>; 40 - }; 41 - export type Handler<HA extends HandlerAuth = never> = ( 42 - ctx: HandlerReqCtx<HA>, 43 - ) => Promise<HandlerOutput> | HandlerOutput;
-138
apps/api/src/xrpc/app/rocksky/feed/getNowPlayings.ts
··· 1 - import type { Context } from "context"; 2 - import { consola } from "consola"; 3 - import { desc, eq, sql } from "drizzle-orm"; 4 - import { Cache, Duration, Effect, pipe } from "effect"; 5 - import type { Server } from "lexicon"; 6 - import type { NowPlayingView } from "lexicon/types/app/rocksky/feed/defs"; 7 - import type { QueryParams } from "lexicon/types/app/rocksky/feed/getNowPlayings"; 8 - import albums from "schema/albums"; 9 - import artists from "schema/artists"; 10 - import scrobbles from "schema/scrobbles"; 11 - import tracks from "schema/tracks"; 12 - import users from "schema/users"; 13 - 14 - export default function (server: Server, ctx: Context) { 15 - const nowPlayingCache = Cache.make({ 16 - capacity: 100, 17 - timeToLive: Duration.seconds(30), 18 - lookup: (params: QueryParams) => 19 - pipe( 20 - { params, ctx }, 21 - retrieve, 22 - Effect.map((data) => ({ data })), 23 - Effect.flatMap(presentation), 24 - Effect.retry({ times: 3 }), 25 - Effect.timeout("120 seconds"), 26 - ), 27 - }); 28 - 29 - const getNowPlayings = (params) => 30 - pipe( 31 - nowPlayingCache, 32 - Effect.flatMap((cache) => cache.get(params)), 33 - Effect.catchAll((err) => { 34 - consola.error(err); 35 - return Effect.succeed({}); 36 - }), 37 - ); 38 - server.app.rocksky.feed.getNowPlayings({ 39 - handler: async ({ params }) => { 40 - const result = await Effect.runPromise(getNowPlayings(params)); 41 - return { 42 - encoding: "application/json", 43 - body: result, 44 - }; 45 - }, 46 - }); 47 - } 48 - 49 - const retrieve = ({ 50 - params, 51 - ctx, 52 - }: { 53 - params: QueryParams; 54 - ctx: Context; 55 - }): Effect.Effect<NowPlayingRecord[], Error, never> => { 56 - return Effect.tryPromise({ 57 - try: () => 58 - ctx.db 59 - .select({ 60 - xataId: scrobbles.id, 61 - trackId: tracks.id, 62 - title: tracks.title, 63 - artist: tracks.artist, 64 - albumArtist: tracks.albumArtist, 65 - album: tracks.album, 66 - albumArt: tracks.albumArt, 67 - handle: users.handle, 68 - did: users.did, 69 - avatar: users.avatar, 70 - uri: scrobbles.uri, 71 - trackUri: tracks.uri, 72 - artistUri: artists.uri, 73 - albumUri: albums.uri, 74 - timestamp: scrobbles.timestamp, 75 - }) 76 - .from(scrobbles) 77 - .leftJoin(artists, eq(scrobbles.artistId, artists.id)) 78 - .leftJoin(albums, eq(scrobbles.albumId, albums.id)) 79 - .leftJoin(tracks, eq(scrobbles.trackId, tracks.id)) 80 - .leftJoin(users, eq(scrobbles.userId, users.id)) 81 - .where( 82 - sql`scrobbles.xata_id IN ( 83 - SELECT DISTINCT ON (inner_s.user_id) inner_s.xata_id 84 - FROM scrobbles inner_s 85 - ORDER BY inner_s.user_id, inner_s.timestamp DESC, inner_s.xata_id DESC 86 - )`, 87 - ) 88 - .orderBy(desc(scrobbles.timestamp)) 89 - .limit(params.size || 20) 90 - .execute(), 91 - catch: (error) => 92 - new Error(`Failed to retrieve now playing songs: ${error}`), 93 - }); 94 - }; 95 - 96 - const presentation = ({ 97 - data, 98 - }: { 99 - data: NowPlayingRecord[]; 100 - }): Effect.Effect<{ nowPlayings: NowPlayingView[] }, never> => { 101 - return Effect.sync(() => ({ 102 - nowPlayings: data.map((record) => ({ 103 - album: record.album, 104 - albumArt: record.albumArt, 105 - albumArtist: record.albumArtist, 106 - albumUri: record.albumUri, 107 - artist: record.artist, 108 - artistUri: record.artistUri, 109 - avatar: record.avatar, 110 - createdAt: record.timestamp.toISOString(), 111 - did: record.did, 112 - handle: record.handle, 113 - id: record.trackId, 114 - title: record.title, 115 - trackId: record.trackId, 116 - trackUri: record.trackUri, 117 - uri: record.uri, 118 - })), 119 - })); 120 - }; 121 - 122 - type NowPlayingRecord = { 123 - xataId: string; 124 - trackId: string; 125 - title: string; 126 - artist: string; 127 - albumArtist: string; 128 - album: string; 129 - albumArt: string; 130 - handle: string; 131 - did: string; 132 - avatar: string; 133 - uri: string; 134 - trackUri: string; 135 - artistUri: string; 136 - albumUri: string; 137 - timestamp: Date; 138 - };
+2 -2
apps/api/src/xrpc/app/rocksky/feed/getStories.ts
··· 26 26 ), 27 27 }); 28 28 29 - const getNowPlayings = (params) => 29 + const getStories = (params: QueryParams) => 30 30 pipe( 31 31 storiesCache, 32 32 Effect.flatMap((cache) => cache.get(params)), ··· 37 37 ); 38 38 server.app.rocksky.feed.getStories({ 39 39 handler: async ({ params }) => { 40 - const result = await Effect.runPromise(getNowPlayings(params)); 40 + const result = await Effect.runPromise(getStories(params)); 41 41 return { 42 42 encoding: "application/json", 43 43 body: result,
-2
apps/api/src/xrpc/index.ts
··· 24 24 import getFilesFromDropbox from "./app/rocksky/dropbox/getFiles"; 25 25 import getMetadataFromDropbox from "./app/rocksky/dropbox/getMetadata"; 26 26 import getTemporaryLinkFromDropbox from "./app/rocksky/dropbox/getTemporaryLink"; 27 - import getNowPlayings from "./app/rocksky/feed/getNowPlayings"; 28 27 import search from "./app/rocksky/feed/search"; 29 28 import downloadFileFromGoogleDrive from "./app/rocksky/googledrive/downloadFile"; 30 29 import getFileFromGoogleDrive from "./app/rocksky/googledrive/getFile"; ··· 113 112 getFilesFromDropbox(server, ctx); 114 113 getMetadataFromDropbox(server, ctx); 115 114 getTemporaryLinkFromDropbox(server, ctx); 116 - getNowPlayings(server, ctx); 117 115 search(server, ctx); 118 116 downloadFileFromGoogleDrive(server, ctx); 119 117 getFilesFromGoogleDrive(server, ctx);