Music streaming on ATProto!

feat: basic lexicon records

ovyerus.com 7579fe19 e115f0d6

verified
+8
.vscode/settings.json
···
+
{
+
"json.schemas": [
+
{
+
"fileMatch": ["/packages/lexicons/**/*.json"],
+
"url": "https://gist.githubusercontent.com/mary-ext/6e428031c18799d1587048b456d118cb/raw/4322c492384ac5da33986dee9588938a88d922f1/schema.json"
+
}
+
]
+
}
+7
bun.lock
···
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
+
"lexmd": "github:espeon/lexmd",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
···
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+
"@atproto/syntax": ["@atproto/syntax@0.4.0", "", {}, "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="],
···
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+
"lexmd": ["lex-md@github:espeon/lexmd#b944125", { "dependencies": { "@atproto/syntax": "^0.4.0", "zod": "^3.24.3" }, "bin": { "lexmd": "dist/main.js" } }, "espeon-lexmd-b944125"],
+
"lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="],
···
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
+
+
"zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+1
package.json
···
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
+
"lexmd": "github:espeon/lexmd",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
+51
packages/lexicons/sh/comet/v0/actor/profile.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.actor.profile",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "A user's Comet profile.",
+
"key": "literal:self",
+
"record": {
+
"type": "object",
+
"properties": {
+
"displayName": {
+
"type": "string",
+
"maxGraphemes": 64,
+
"maxLength": 640
+
},
+
"description": {
+
"type": "string",
+
"description": "Free-form profile description text.",
+
"maxGraphemes": 256,
+
"maxLength": 2560
+
},
+
"descriptionFacets": {
+
"type": "ref",
+
"description": "Annotations of the user's description.",
+
"ref": "sh.comet.v0.richtext.facets"
+
},
+
"avatar": {
+
"type": "blob",
+
"description": "Small image to be displayed next to posts from account. AKA, 'profile picture'",
+
"accept": ["image/png", "image/jpeg"],
+
"maxSize": 1000000
+
},
+
"featured": {
+
"type": "array",
+
"items": {
+
"type": "ref",
+
"ref": "com.atproto.repo.strongRef"
+
}
+
},
+
"createdAt": { "type": "string", "format": "datetime" }
+
}
+
}
+
},
+
"view": {},
+
"viewBasic": {
+
"type": "object",
+
"properties": {}
+
}
+
}
+
}
+1
packages/lexicons/sh/comet/v0/feed/comment.json
···
+
{}
+54
packages/lexicons/sh/comet/v0/feed/defs.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.defs",
+
"defs": {
+
"tag": {
+
"type": "string",
+
"knownValues": [
+
"sh.comet.v0.feed.tag#alternative",
+
"sh.comet.v0.feed.tag#ambient",
+
"sh.comet.v0.feed.tag#electronic",
+
"sh.comet.v0.feed.tag#experimental",
+
"sh.comet.v0.feed.tag#hiphop",
+
"sh.comet.v0.feed.tag#metal",
+
"sh.comet.v0.feed.tag#rock",
+
"sh.comet.v0.feed.tag#podcast",
+
"sh.comet.v0.feed.tag#pop",
+
"sh.comet.v0.feed.tag#punk"
+
]
+
},
+
"link": {
+
"type": "object",
+
"description": "Link for the track. Usually to acquire it in some way, e.g. via free download or purchase. | TODO: multiple links?",
+
"required": ["type", "value"],
+
"properties": {
+
"type": {
+
"type": "string",
+
"knownValues": [
+
"sh.comet.v0.feed.defs#downloadLink",
+
"sh.comet.v0.feed.defs#buyLink"
+
]
+
},
+
"value": { "type": "string", "format": "uri" }
+
}
+
},
+
"downloadLink": {
+
"type": "token",
+
"description": "Indicate the link leads to a free download for the track."
+
},
+
"buyLink": {
+
"type": "token",
+
"description": "Indicate the link leads to a purchase page for the track."
+
},
+
"viewerState": {
+
"type": "object",
+
"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" }
+
// TODO: pinned items
+
// TODO: maybe what personal playlists it's in?
+
}
+
}
+
}
+
}
+19
packages/lexicons/sh/comet/v0/feed/like.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.like",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "Record representing a 'like' of some media.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["subject", "createdAt"],
+
"properties": {
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
+
"createdAt": { "type": "string", "format": "datetime" }
+
}
+
}
+
}
+
}
+
}
+19
packages/lexicons/sh/comet/v0/feed/play.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.play",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "Record representing a 'play' of some media.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["subject", "createdAt"],
+
"properties": {
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
+
"createdAt": { "type": "string", "format": "datetime" }
+
}
+
}
+
}
+
}
+
}
+83
packages/lexicons/sh/comet/v0/feed/playlist.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.playlist",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "A Comet playlist, containing many audio tracks.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["tracks", "title", "type", "createdAt"],
+
"properties": {
+
"tracks": {
+
"type": "array",
+
"minLength": 1,
+
"items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
+
},
+
"image": {
+
"type": "blob",
+
"description": "Image to be displayed representing the playlist.",
+
"accept": ["image/png", "image/jpeg"],
+
"maxSize": 1000000
+
},
+
"title": {
+
"type": "string",
+
"description": "Title of the playlist.",
+
"minLength": 1,
+
"maxLength": 2560,
+
"maxGraphemes": 256
+
},
+
"description": {
+
"type": "string",
+
"description": "Description of the playlist.",
+
"maxLength": 20000,
+
"maxGraphemes": 2000
+
},
+
"type": {
+
"type": "string",
+
"description": "Type of the playlist. Allows differentiating between playlist's different purposes.",
+
"knownValues": [
+
"sh.comet.v0.feed.playlist#album",
+
"sh.comet.v0.feed.playlist#compilation",
+
"sh.comet.v0.feed.playlist#playlist",
+
"sh.comet.v0.feed.playlist#podcast"
+
]
+
},
+
"tags": {
+
"type": "array",
+
"description": "Tags/genres for the playlist. First item is used as the \"primary\" tag. | TODO: reconsider maxLength value",
+
"minLength": 1,
+
"maxLength": 5,
+
"items": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" }
+
},
+
"link": {
+
"type": "ref",
+
"ref": "sh.comet.v0.feed.defs#link"
+
},
+
"createdAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "Timestamp for when the playlist was originally created."
+
}
+
}
+
}
+
},
+
"album": {
+
"type": "token",
+
"description": "Indicates the playlist is an album or extended play."
+
},
+
"compilation": {
+
"type": "token",
+
"description": "Indicates the playlist is a compilation of various tracks, usually put together by a label."
+
},
+
"playlist": {
+
"type": "token",
+
"description": "Indicates the playlist is a miscellaneous list of tracks."
+
},
+
"podcast": {
+
"type": "token",
+
"description": "Indicates the playlist is a podcast or radio show split into several individual tracks."
+
}
+
}
+
}
+19
packages/lexicons/sh/comet/v0/feed/repost.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.like",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "Record representing a 'repost' of some media.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["subject", "createdAt"],
+
"properties": {
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
+
"createdAt": { "type": "string", "format": "datetime" }
+
}
+
}
+
}
+
}
+
}
+17
packages/lexicons/sh/comet/v0/feed/tag.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.tag",
+
"description": "TODO: maybe instead of being like this, make tag a proper record?",
+
"defs": {
+
"alternative": { "type": "token" },
+
"ambient": { "type": "token" },
+
"electronic": { "type": "token" },
+
"experimental": { "type": "token" },
+
"hiphop": { "type": "token" },
+
"metal": { "type": "token" },
+
"rock": { "type": "token" },
+
"podcast": { "type": "token" },
+
"pop": { "type": "token" },
+
"punk": { "type": "token" }
+
}
+
}
+91
packages/lexicons/sh/comet/v0/feed/track.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.feed.track",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "A Comet audio track.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["audio", "title", "createdAt"],
+
"properties": {
+
"audio": {
+
"type": "blob",
+
"description": "Audio of the track, ideally encoded as 96k Opus. Limited to 100mb.",
+
"accept": ["audio/ogg"],
+
"maxSize": 100000000
+
},
+
"image": {
+
"type": "blob",
+
"description": "Image to be displayed representing the track.",
+
"accept": ["image/png", "image/jpeg"],
+
"maxSize": 1000000
+
},
+
"title": {
+
"type": "string",
+
"description": "Title of the track. Usually shouldn't include the creator's name.",
+
"minLength": 1,
+
"maxLength": 2560,
+
"maxGraphemes": 256
+
},
+
"description": {
+
"type": "string",
+
"description": "Description of the track.",
+
"maxLength": 20000,
+
"maxGraphemes": 2000
+
},
+
"descriptionFacets": {
+
"type": "ref",
+
"description": "Annotations of the track's description.",
+
"ref": "sh.comet.v0.richtext.facets"
+
},
+
"tags": {
+
"type": "array",
+
"description": "Tags/genres for the track. First item is used as the \"primary\" tag. | TODO: reconsider maxLength value",
+
"minLength": 1,
+
"maxLength": 5,
+
"items": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" }
+
},
+
"link": {
+
"type": "ref",
+
"ref": "sh.comet.v0.feed.defs#link"
+
},
+
"createdAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "Timestamp for when the track was originally created/released."
+
}
+
}
+
}
+
},
+
"view": {
+
"type": "object",
+
"required": ["uri", "cid", "author", "audio", "record", "indexedAt"],
+
"properties": {
+
"uri": { "type": "string", "format": "at-uri" },
+
"cid": { "type": "string", "format": "cid" },
+
"author": { "type": "ref", "ref": "TODO" },
+
"audio": {
+
"type": "string",
+
"format": "uri",
+
"description": "URL pointing to where the audio data for the track can be fetched. May be re-encoded from the original blob."
+
},
+
"image": {
+
"type": "string",
+
"format": "uri",
+
"description": "URL pointing to where the image for the track can be fetched."
+
},
+
"record": {
+
"type": "ref",
+
"ref": "#main"
+
},
+
"repostCount": { "type": "integer" },
+
"likeCount": { "type": "integer" },
+
"playCount": { "type": "integer" },
+
"indexedAt": { "type": "string", "format": "datetime" },
+
"viewer": { "type": "ref", "ref": "sh.comet.v0.feed.defs#viewerState" }
+
}
+
}
+
}
+
}
+51
packages/lexicons/sh/comet/v0/richtext/facets.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.comet.v0.richtext.facets",
+
"defs": {
+
"main": {
+
"type": "object",
+
"description": "Annotation of a sub-string within rich text.",
+
"required": ["index", "features"],
+
"properties": {
+
"index": { "type": "ref", "ref": "#byteSlice" },
+
"features": {
+
"type": "array",
+
"items": { "type": "union", "refs": ["#mention", "#link", "#tag"] }
+
}
+
}
+
},
+
"mention": {
+
"type": "object",
+
"description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.",
+
"required": ["did"],
+
"properties": {
+
"did": { "type": "string", "format": "did" }
+
}
+
},
+
"link": {
+
"type": "object",
+
"description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.",
+
"required": ["uri"],
+
"properties": {
+
"uri": { "type": "string", "format": "uri" }
+
}
+
},
+
"tag": {
+
"type": "object",
+
"description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags').",
+
"required": ["tag"],
+
"properties": {
+
"tag": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" }
+
}
+
},
+
"byteSlice": {
+
"type": "object",
+
"description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.",
+
"required": ["byteStart", "byteEnd"],
+
"properties": {
+
"byteStart": { "type": "integer", "minimum": 0 },
+
"byteEnd": { "type": "integer", "minimum": 0 }
+
}
+
}
+
}
+
}