Music streaming on ATProto!

feat(lexicons): getProfiles, getActorPlaylisst, getActorTracks

ovyerus.com feca8ceb 9e090d4b

verified
+37
packages/lexicons/defs/sh/comet/v0/actor/getProfiles.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.actor.getProfiles",
+
"defs": {
+
"main": {
+
"type": "query",
+
"description": "Get the profile views of multiple actors.",
+
"parameters": {
+
"type": "params",
+
"required": ["actors"],
+
"properties": {
+
"actors": {
+
"type": "array",
+
"maxLength": 25,
+
"items": { "type": "string", "format": "at-identifier" }
+
}
+
}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["profiles"],
+
"properties": {
+
"profiles": {
+
"type": "array",
+
"items": {
+
"type": "ref",
+
"ref": "sh.comet.v0.actor.profile#view"
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+7 -11
packages/lexicons/defs/sh/comet/v0/actor/profile.json
···
"maxGraphemes": 64,
"maxLength": 640
},
-
"description": {
-
"type": "string",
-
"maxGraphemes": 256,
-
"maxLength": 2560
-
},
-
"descriptionFacets": {
-
"type": "ref",
-
"ref": "sh.comet.v0.richtext.facet"
-
},
"avatar": { "type": "string", "format": "uri" },
"indexedAt": { "type": "string", "format": "datetime" },
"createdAt": { "type": "string", "format": "datetime" },
-
"viewer": { "type": "unknown", "description": "TODO" }
+
"viewer": { "type": "ref", "ref": "#viewerState" }
}
},
"viewFull": {
···
"maxLength": 5,
"items": { "type": "string", "format": "at-uri" }
},
-
"viewer": { "type": "unknown", "description": "TODO" }
+
"viewer": { "type": "ref", "ref": "#viewerState" }
}
+
},
+
"viewerState": {
+
"type": "object",
+
"description": "Metadata about the requesting account's relationship with the user. TODO: determine if we create our own graph or inherit bsky's.",
+
"properties": {}
}
}
}
+2 -1
packages/lexicons/defs/sh/comet/v0/feed/defs.json
···
"description": "Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests.",
"properties": {
"like": { "type": "string", "format": "at-uri" },
-
"repost": { "type": "string", "format": "at-uri" }
+
"repost": { "type": "string", "format": "at-uri" },
+
"featured": { "type": "boolean" }
}
}
}
+44
packages/lexicons/defs/sh/comet/v0/feed/getActorPlaylists.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.getActorPlaylists",
+
"defs": {
+
"main": {
+
"type": "query",
+
"description": "Get a list of an actor's playlists.",
+
"parameters": {
+
"type": "params",
+
"required": ["actor"],
+
"properties": {
+
"actor": {
+
"type": "string",
+
"format": "at-identifier"
+
},
+
"limit": {
+
"type": "integer",
+
"minimum": 1,
+
"maximum": 100,
+
"default": 50
+
},
+
"cursor": { "type": "string" }
+
}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["playlists"],
+
"properties": {
+
"cursor": { "type": "string" },
+
"playlists": {
+
"type": "array",
+
"items": {
+
"type": "ref",
+
"ref": "sh.comet.v0.feed.playlist#view"
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+44
packages/lexicons/defs/sh/comet/v0/feed/getActorTracks.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.getActorTracks",
+
"defs": {
+
"main": {
+
"type": "query",
+
"description": "Get a list of an actor's tracks.",
+
"parameters": {
+
"type": "params",
+
"required": ["actor"],
+
"properties": {
+
"actor": {
+
"type": "string",
+
"format": "at-identifier"
+
},
+
"limit": {
+
"type": "integer",
+
"minimum": 1,
+
"maximum": 100,
+
"default": 50
+
},
+
"cursor": { "type": "string" }
+
}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["tracks"],
+
"properties": {
+
"cursor": { "type": "string" },
+
"tracks": {
+
"type": "array",
+
"items": {
+
"type": "ref",
+
"ref": "sh.comet.v0.feed.track#view"
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+32 -1
packages/lexicons/defs/sh/comet/v0/feed/playlist.json
···
"properties": {
"tracks": {
"type": "array",
-
"minLength": 1,
"items": { "type": "string", "format": "at-uri" }
},
"image": {
···
"podcast": {
"type": "token",
"description": "Indicates the playlist is a podcast or radio show split into several individual tracks."
+
},
+
"view": {
+
"type": "object",
+
"required": ["uri", "cid", "author", "tracks", "record"],
+
"properties": {
+
"uri": { "type": "string", "format": "at-uri" },
+
"cid": { "type": "string", "format": "cid" },
+
"author": {
+
"type": "ref",
+
"ref": "sh.comet.v0.actor.profile#viewFull"
+
},
+
"tracks": {
+
"type": "array",
+
"items": { "type": "ref", "ref": "sh.comet.v0.feed.track#view" },
+
"description": "TODO: include cursor for pagination if too many?"
+
},
+
"image": {
+
"type": "string",
+
"format": "uri",
+
"description": "URL pointing to where the image for the playlist can be fetched."
+
},
+
"record": {
+
"type": "ref",
+
"ref": "#main"
+
},
+
"repostCount": { "type": "integer" },
+
"likeCount": { "type": "integer" },
+
"commentCount": { "type": "integer" },
+
"trackCount": { "type": "integer" },
+
"indexedAt": { "type": "string", "format": "datetime" },
+
"viewer": { "type": "ref", "ref": "sh.comet.v0.feed.defs#viewerState" }
+
}
}
}
}
+6 -2
packages/lexicons/defs/sh/comet/v0/feed/track.json
···
"defs": {
"main": {
"type": "record",
-
"description": "A Comet audio track.",
+
"description": "A Comet audio track. TODO: should probably have some sort of pre-calculated waveform, or have a query to get one from a blob?",
"key": "tid",
"record": {
"type": "object",
···
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" },
-
"author": { "type": "unknown", "description": "TODO" },
+
"author": {
+
"type": "ref",
+
"ref": "sh.comet.v0.actor.profile#viewFull"
+
},
"audio": {
"type": "string",
"format": "uri",
···
"repostCount": { "type": "integer" },
"likeCount": { "type": "integer" },
"playCount": { "type": "integer" },
+
"commentCount": { "type": "integer" },
"indexedAt": { "type": "string", "format": "datetime" },
"viewer": { "type": "ref", "ref": "sh.comet.v0.feed.defs#viewerState" }
}
+1 -1
packages/lexicons/package.json
···
{
"name": "@comet/lexicons",
-
"module": "index.ts",
+
"main": "src/index.ts",
"devDependencies": {
"@atcute/lex-cli": "^2.0.2",
"@types/bun": "latest"
+3
packages/lexicons/src/index.ts
···
export * as ShCometV0ActorGetProfile from "./types/sh/comet/v0/actor/getProfile.js";
+
export * as ShCometV0ActorGetProfiles from "./types/sh/comet/v0/actor/getProfiles.js";
export * as ShCometV0ActorProfile from "./types/sh/comet/v0/actor/profile.js";
export * as ShCometV0FeedComment from "./types/sh/comet/v0/feed/comment.js";
export * as ShCometV0FeedDefs from "./types/sh/comet/v0/feed/defs.js";
+
export * as ShCometV0FeedGetActorPlaylists from "./types/sh/comet/v0/feed/getActorPlaylists.js";
+
export * as ShCometV0FeedGetActorTracks from "./types/sh/comet/v0/feed/getActorTracks.js";
export * as ShCometV0FeedLike from "./types/sh/comet/v0/feed/like.js";
export * as ShCometV0FeedPlay from "./types/sh/comet/v0/feed/play.js";
export * as ShCometV0FeedPlaylist from "./types/sh/comet/v0/feed/playlist.js";
+33
packages/lexicons/src/types/sh/comet/v0/actor/getProfiles.ts
···
+
import type {} from "@atcute/lexicons";
+
import * as v from "@atcute/lexicons/validations";
+
import type {} from "@atcute/lexicons/ambient";
+
import * as ShCometV0ActorProfile from "./profile.js";
+
+
const _mainSchema = /*#__PURE__*/ v.query("sh.comet.v0.actor.getProfiles", {
+
params: /*#__PURE__*/ v.object({
+
actors: /*#__PURE__*/ v.constrain(
+
/*#__PURE__*/ v.array(/*#__PURE__*/ v.actorIdentifierString()),
+
[/*#__PURE__*/ v.arrayLength(0, 25)],
+
),
+
}),
+
output: {
+
type: "lex",
+
schema: /*#__PURE__*/ v.object({
+
get profiles() {
+
return /*#__PURE__*/ v.array(ShCometV0ActorProfile.viewSchema);
+
},
+
}),
+
},
+
});
+
+
type main$schematype = typeof _mainSchema;
+
+
export interface mainSchema extends main$schematype {}
+
+
export const mainSchema = _mainSchema as mainSchema;
+
+
declare module "@atcute/lexicons/ambient" {
+
interface XRPCQueries {
+
"sh.comet.v0.actor.getProfiles": mainSchema;
+
}
+
}
+15 -11
packages/lexicons/src/types/sh/comet/v0/actor/profile.ts
···
),
avatar: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
-
description: /*#__PURE__*/ v.optional(
-
/*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [
-
/*#__PURE__*/ v.stringLength(0, 2560),
-
/*#__PURE__*/ v.stringGraphemes(0, 256),
-
]),
-
),
-
get descriptionFacets() {
-
return /*#__PURE__*/ v.optional(ShCometV0RichtextFacet.mainSchema);
-
},
did: /*#__PURE__*/ v.didString(),
displayName: /*#__PURE__*/ v.optional(
/*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [
···
),
handle: /*#__PURE__*/ v.handleString(),
indexedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
-
viewer: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()),
+
get viewer() {
+
return /*#__PURE__*/ v.optional(viewerStateSchema);
+
},
});
const _viewFullSchema = /*#__PURE__*/ v.object({
$type: /*#__PURE__*/ v.optional(
···
indexedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
playlistsCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
tracksCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
-
viewer: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()),
+
get viewer() {
+
return /*#__PURE__*/ v.optional(viewerStateSchema);
+
},
+
});
+
const _viewerStateSchema = /*#__PURE__*/ v.object({
+
$type: /*#__PURE__*/ v.optional(
+
/*#__PURE__*/ v.literal("sh.comet.v0.actor.profile#viewerState"),
+
),
});
type main$schematype = typeof _mainSchema;
type view$schematype = typeof _viewSchema;
type viewFull$schematype = typeof _viewFullSchema;
+
type viewerState$schematype = typeof _viewerStateSchema;
export interface mainSchema extends main$schematype {}
export interface viewSchema extends view$schematype {}
export interface viewFullSchema extends viewFull$schematype {}
+
export interface viewerStateSchema extends viewerState$schematype {}
export const mainSchema = _mainSchema as mainSchema;
export const viewSchema = _viewSchema as viewSchema;
export const viewFullSchema = _viewFullSchema as viewFullSchema;
+
export const viewerStateSchema = _viewerStateSchema as viewerStateSchema;
export interface Main extends v.InferInput<typeof mainSchema> {}
export interface View extends v.InferInput<typeof viewSchema> {}
export interface ViewFull extends v.InferInput<typeof viewFullSchema> {}
+
export interface ViewerState extends v.InferInput<typeof viewerStateSchema> {}
declare module "@atcute/lexicons/ambient" {
interface Records {
+1
packages/lexicons/src/types/sh/comet/v0/feed/defs.ts
···
$type: /*#__PURE__*/ v.optional(
/*#__PURE__*/ v.literal("sh.comet.v0.feed.defs#viewerState"),
),
+
featured: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
like: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()),
repost: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()),
});
+41
packages/lexicons/src/types/sh/comet/v0/feed/getActorPlaylists.ts
···
+
import type {} from "@atcute/lexicons";
+
import * as v from "@atcute/lexicons/validations";
+
import type {} from "@atcute/lexicons/ambient";
+
import * as ShCometV0FeedPlaylist from "./playlist.js";
+
+
const _mainSchema = /*#__PURE__*/ v.query(
+
"sh.comet.v0.feed.getActorPlaylists",
+
{
+
params: /*#__PURE__*/ v.object({
+
actor: /*#__PURE__*/ v.actorIdentifierString(),
+
cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+
limit: /*#__PURE__*/ v.optional(
+
/*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [
+
/*#__PURE__*/ v.integerRange(1, 100),
+
]),
+
50,
+
),
+
}),
+
output: {
+
type: "lex",
+
schema: /*#__PURE__*/ v.object({
+
cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+
get playlists() {
+
return /*#__PURE__*/ v.array(ShCometV0FeedPlaylist.viewSchema);
+
},
+
}),
+
},
+
},
+
);
+
+
type main$schematype = typeof _mainSchema;
+
+
export interface mainSchema extends main$schematype {}
+
+
export const mainSchema = _mainSchema as mainSchema;
+
+
declare module "@atcute/lexicons/ambient" {
+
interface XRPCQueries {
+
"sh.comet.v0.feed.getActorPlaylists": mainSchema;
+
}
+
}
+38
packages/lexicons/src/types/sh/comet/v0/feed/getActorTracks.ts
···
+
import type {} from "@atcute/lexicons";
+
import * as v from "@atcute/lexicons/validations";
+
import type {} from "@atcute/lexicons/ambient";
+
import * as ShCometV0FeedTrack from "./track.js";
+
+
const _mainSchema = /*#__PURE__*/ v.query("sh.comet.v0.feed.getActorTracks", {
+
params: /*#__PURE__*/ v.object({
+
actor: /*#__PURE__*/ v.actorIdentifierString(),
+
cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+
limit: /*#__PURE__*/ v.optional(
+
/*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [
+
/*#__PURE__*/ v.integerRange(1, 100),
+
]),
+
50,
+
),
+
}),
+
output: {
+
type: "lex",
+
schema: /*#__PURE__*/ v.object({
+
cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+
get tracks() {
+
return /*#__PURE__*/ v.array(ShCometV0FeedTrack.viewSchema);
+
},
+
}),
+
},
+
});
+
+
type main$schematype = typeof _mainSchema;
+
+
export interface mainSchema extends main$schematype {}
+
+
export const mainSchema = _mainSchema as mainSchema;
+
+
declare module "@atcute/lexicons/ambient" {
+
interface XRPCQueries {
+
"sh.comet.v0.feed.getActorTracks": mainSchema;
+
}
+
}
+32 -4
packages/lexicons/src/types/sh/comet/v0/feed/playlist.ts
···
import type {} from "@atcute/lexicons";
import * as v from "@atcute/lexicons/validations";
import type {} from "@atcute/lexicons/ambient";
+
import * as ShCometV0ActorProfile from "../actor/profile.js";
import * as ShCometV0FeedDefs from "./defs.js";
+
import * as ShCometV0FeedTrack from "./track.js";
import * as ShCometV0RichtextFacet from "../richtext/facet.js";
const _albumSchema = /*#__PURE__*/ v.literal("sh.comet.v0.feed.playlist#album");
···
/*#__PURE__*/ v.stringLength(1, 2560),
/*#__PURE__*/ v.stringGraphemes(0, 256),
]),
-
tracks: /*#__PURE__*/ v.constrain(
-
/*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()),
-
[/*#__PURE__*/ v.arrayLength(1)],
-
),
+
tracks: /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()),
type: /*#__PURE__*/ v.string<
| "sh.comet.v0.feed.playlist#album"
| "sh.comet.v0.feed.playlist#compilation"
···
const _podcastSchema = /*#__PURE__*/ v.literal(
"sh.comet.v0.feed.playlist#podcast",
);
+
const _viewSchema = /*#__PURE__*/ v.object({
+
$type: /*#__PURE__*/ v.optional(
+
/*#__PURE__*/ v.literal("sh.comet.v0.feed.playlist#view"),
+
),
+
get author() {
+
return ShCometV0ActorProfile.viewFullSchema;
+
},
+
cid: /*#__PURE__*/ v.cidString(),
+
commentCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+
image: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+
indexedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+
likeCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+
get record() {
+
return mainSchema;
+
},
+
repostCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+
trackCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+
get tracks() {
+
return /*#__PURE__*/ v.array(ShCometV0FeedTrack.viewSchema);
+
},
+
uri: /*#__PURE__*/ v.resourceUriString(),
+
get viewer() {
+
return /*#__PURE__*/ v.optional(ShCometV0FeedDefs.viewerStateSchema);
+
},
+
});
type album$schematype = typeof _albumSchema;
type compilation$schematype = typeof _compilationSchema;
type main$schematype = typeof _mainSchema;
type playlist$schematype = typeof _playlistSchema;
type podcast$schematype = typeof _podcastSchema;
+
type view$schematype = typeof _viewSchema;
export interface albumSchema extends album$schematype {}
export interface compilationSchema extends compilation$schematype {}
export interface mainSchema extends main$schematype {}
export interface playlistSchema extends playlist$schematype {}
export interface podcastSchema extends podcast$schematype {}
+
export interface viewSchema extends view$schematype {}
export const albumSchema = _albumSchema as albumSchema;
export const compilationSchema = _compilationSchema as compilationSchema;
export const mainSchema = _mainSchema as mainSchema;
export const playlistSchema = _playlistSchema as playlistSchema;
export const podcastSchema = _podcastSchema as podcastSchema;
+
export const viewSchema = _viewSchema as viewSchema;
export type Album = v.InferInput<typeof albumSchema>;
export type Compilation = v.InferInput<typeof compilationSchema>;
export interface Main extends v.InferInput<typeof mainSchema> {}
export type Playlist = v.InferInput<typeof playlistSchema>;
export type Podcast = v.InferInput<typeof podcastSchema>;
+
export interface View extends v.InferInput<typeof viewSchema> {}
declare module "@atcute/lexicons/ambient" {
interface Records {
+5 -1
packages/lexicons/src/types/sh/comet/v0/feed/track.ts
···
import type {} from "@atcute/lexicons";
import * as v from "@atcute/lexicons/validations";
import type {} from "@atcute/lexicons/ambient";
+
import * as ShCometV0ActorProfile from "../actor/profile.js";
import * as ShCometV0FeedDefs from "./defs.js";
import * as ShCometV0RichtextFacet from "../richtext/facet.js";
···
/*#__PURE__*/ v.literal("sh.comet.v0.feed.track#view"),
),
audio: /*#__PURE__*/ v.genericUriString(),
-
author: /*#__PURE__*/ v.unknown(),
+
get author() {
+
return ShCometV0ActorProfile.viewFullSchema;
+
},
cid: /*#__PURE__*/ v.cidString(),
+
commentCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
image: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
indexedAt: /*#__PURE__*/ v.datetimeString(),
likeCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),