Music streaming on ATProto!

feat: basic lexicon records

ovyerus.com 7579fe19 e115f0d6

verified
+421
+8
.vscode/settings.json
··· 1 + { 2 + "json.schemas": [ 3 + { 4 + "fileMatch": ["/packages/lexicons/**/*.json"], 5 + "url": "https://gist.githubusercontent.com/mary-ext/6e428031c18799d1587048b456d118cb/raw/4322c492384ac5da33986dee9588938a88d922f1/schema.json" 6 + } 7 + ] 8 + }
+7
bun.lock
··· 18 18 "eslint-config-prettier": "^10.0.1", 19 19 "eslint-plugin-svelte": "^3.0.0", 20 20 "globals": "^16.0.0", 21 + "lexmd": "github:espeon/lexmd", 21 22 "prettier": "^3.4.2", 22 23 "prettier-plugin-svelte": "^3.3.3", 23 24 "prettier-plugin-tailwindcss": "^0.6.11", ··· 32 33 }, 33 34 "packages": { 34 35 "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], 36 + 37 + "@atproto/syntax": ["@atproto/syntax@0.4.0", "", {}, "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA=="], 35 38 36 39 "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="], 37 40 ··· 397 400 398 401 "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], 399 402 403 + "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"], 404 + 400 405 "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=="], 401 406 402 407 "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="], ··· 560 565 "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 561 566 562 567 "zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="], 568 + 569 + "zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="], 563 570 564 571 "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 565 572
+1
package.json
··· 28 28 "eslint-config-prettier": "^10.0.1", 29 29 "eslint-plugin-svelte": "^3.0.0", 30 30 "globals": "^16.0.0", 31 + "lexmd": "github:espeon/lexmd", 31 32 "prettier": "^3.4.2", 32 33 "prettier-plugin-svelte": "^3.3.3", 33 34 "prettier-plugin-tailwindcss": "^0.6.11",
+51
packages/lexicons/sh/comet/v0/actor/profile.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.actor.profile", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "A user's Comet profile.", 8 + "key": "literal:self", 9 + "record": { 10 + "type": "object", 11 + "properties": { 12 + "displayName": { 13 + "type": "string", 14 + "maxGraphemes": 64, 15 + "maxLength": 640 16 + }, 17 + "description": { 18 + "type": "string", 19 + "description": "Free-form profile description text.", 20 + "maxGraphemes": 256, 21 + "maxLength": 2560 22 + }, 23 + "descriptionFacets": { 24 + "type": "ref", 25 + "description": "Annotations of the user's description.", 26 + "ref": "sh.comet.v0.richtext.facets" 27 + }, 28 + "avatar": { 29 + "type": "blob", 30 + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'", 31 + "accept": ["image/png", "image/jpeg"], 32 + "maxSize": 1000000 33 + }, 34 + "featured": { 35 + "type": "array", 36 + "items": { 37 + "type": "ref", 38 + "ref": "com.atproto.repo.strongRef" 39 + } 40 + }, 41 + "createdAt": { "type": "string", "format": "datetime" } 42 + } 43 + } 44 + }, 45 + "view": {}, 46 + "viewBasic": { 47 + "type": "object", 48 + "properties": {} 49 + } 50 + } 51 + }
+1
packages/lexicons/sh/comet/v0/feed/comment.json
··· 1 + {}
+54
packages/lexicons/sh/comet/v0/feed/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.defs", 4 + "defs": { 5 + "tag": { 6 + "type": "string", 7 + "knownValues": [ 8 + "sh.comet.v0.feed.tag#alternative", 9 + "sh.comet.v0.feed.tag#ambient", 10 + "sh.comet.v0.feed.tag#electronic", 11 + "sh.comet.v0.feed.tag#experimental", 12 + "sh.comet.v0.feed.tag#hiphop", 13 + "sh.comet.v0.feed.tag#metal", 14 + "sh.comet.v0.feed.tag#rock", 15 + "sh.comet.v0.feed.tag#podcast", 16 + "sh.comet.v0.feed.tag#pop", 17 + "sh.comet.v0.feed.tag#punk" 18 + ] 19 + }, 20 + "link": { 21 + "type": "object", 22 + "description": "Link for the track. Usually to acquire it in some way, e.g. via free download or purchase. | TODO: multiple links?", 23 + "required": ["type", "value"], 24 + "properties": { 25 + "type": { 26 + "type": "string", 27 + "knownValues": [ 28 + "sh.comet.v0.feed.defs#downloadLink", 29 + "sh.comet.v0.feed.defs#buyLink" 30 + ] 31 + }, 32 + "value": { "type": "string", "format": "uri" } 33 + } 34 + }, 35 + "downloadLink": { 36 + "type": "token", 37 + "description": "Indicate the link leads to a free download for the track." 38 + }, 39 + "buyLink": { 40 + "type": "token", 41 + "description": "Indicate the link leads to a purchase page for the track." 42 + }, 43 + "viewerState": { 44 + "type": "object", 45 + "description": "Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests.", 46 + "properties": { 47 + "like": { "type": "string", "format": "at-uri" }, 48 + "repost": { "type": "string", "format": "at-uri" } 49 + // TODO: pinned items 50 + // TODO: maybe what personal playlists it's in? 51 + } 52 + } 53 + } 54 + }
+19
packages/lexicons/sh/comet/v0/feed/like.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.like", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Record representing a 'like' of some media.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["subject", "createdAt"], 12 + "properties": { 13 + "subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, 14 + "createdAt": { "type": "string", "format": "datetime" } 15 + } 16 + } 17 + } 18 + } 19 + }
+19
packages/lexicons/sh/comet/v0/feed/play.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.play", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Record representing a 'play' of some media.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["subject", "createdAt"], 12 + "properties": { 13 + "subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, 14 + "createdAt": { "type": "string", "format": "datetime" } 15 + } 16 + } 17 + } 18 + } 19 + }
+83
packages/lexicons/sh/comet/v0/feed/playlist.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.playlist", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "A Comet playlist, containing many audio tracks.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["tracks", "title", "type", "createdAt"], 12 + "properties": { 13 + "tracks": { 14 + "type": "array", 15 + "minLength": 1, 16 + "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" } 17 + }, 18 + "image": { 19 + "type": "blob", 20 + "description": "Image to be displayed representing the playlist.", 21 + "accept": ["image/png", "image/jpeg"], 22 + "maxSize": 1000000 23 + }, 24 + "title": { 25 + "type": "string", 26 + "description": "Title of the playlist.", 27 + "minLength": 1, 28 + "maxLength": 2560, 29 + "maxGraphemes": 256 30 + }, 31 + "description": { 32 + "type": "string", 33 + "description": "Description of the playlist.", 34 + "maxLength": 20000, 35 + "maxGraphemes": 2000 36 + }, 37 + "type": { 38 + "type": "string", 39 + "description": "Type of the playlist. Allows differentiating between playlist's different purposes.", 40 + "knownValues": [ 41 + "sh.comet.v0.feed.playlist#album", 42 + "sh.comet.v0.feed.playlist#compilation", 43 + "sh.comet.v0.feed.playlist#playlist", 44 + "sh.comet.v0.feed.playlist#podcast" 45 + ] 46 + }, 47 + "tags": { 48 + "type": "array", 49 + "description": "Tags/genres for the playlist. First item is used as the \"primary\" tag. | TODO: reconsider maxLength value", 50 + "minLength": 1, 51 + "maxLength": 5, 52 + "items": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" } 53 + }, 54 + "link": { 55 + "type": "ref", 56 + "ref": "sh.comet.v0.feed.defs#link" 57 + }, 58 + "createdAt": { 59 + "type": "string", 60 + "format": "datetime", 61 + "description": "Timestamp for when the playlist was originally created." 62 + } 63 + } 64 + } 65 + }, 66 + "album": { 67 + "type": "token", 68 + "description": "Indicates the playlist is an album or extended play." 69 + }, 70 + "compilation": { 71 + "type": "token", 72 + "description": "Indicates the playlist is a compilation of various tracks, usually put together by a label." 73 + }, 74 + "playlist": { 75 + "type": "token", 76 + "description": "Indicates the playlist is a miscellaneous list of tracks." 77 + }, 78 + "podcast": { 79 + "type": "token", 80 + "description": "Indicates the playlist is a podcast or radio show split into several individual tracks." 81 + } 82 + } 83 + }
+19
packages/lexicons/sh/comet/v0/feed/repost.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.like", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Record representing a 'repost' of some media.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["subject", "createdAt"], 12 + "properties": { 13 + "subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, 14 + "createdAt": { "type": "string", "format": "datetime" } 15 + } 16 + } 17 + } 18 + } 19 + }
+17
packages/lexicons/sh/comet/v0/feed/tag.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.tag", 4 + "description": "TODO: maybe instead of being like this, make tag a proper record?", 5 + "defs": { 6 + "alternative": { "type": "token" }, 7 + "ambient": { "type": "token" }, 8 + "electronic": { "type": "token" }, 9 + "experimental": { "type": "token" }, 10 + "hiphop": { "type": "token" }, 11 + "metal": { "type": "token" }, 12 + "rock": { "type": "token" }, 13 + "podcast": { "type": "token" }, 14 + "pop": { "type": "token" }, 15 + "punk": { "type": "token" } 16 + } 17 + }
+91
packages/lexicons/sh/comet/v0/feed/track.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.feed.track", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "A Comet audio track.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["audio", "title", "createdAt"], 12 + "properties": { 13 + "audio": { 14 + "type": "blob", 15 + "description": "Audio of the track, ideally encoded as 96k Opus. Limited to 100mb.", 16 + "accept": ["audio/ogg"], 17 + "maxSize": 100000000 18 + }, 19 + "image": { 20 + "type": "blob", 21 + "description": "Image to be displayed representing the track.", 22 + "accept": ["image/png", "image/jpeg"], 23 + "maxSize": 1000000 24 + }, 25 + "title": { 26 + "type": "string", 27 + "description": "Title of the track. Usually shouldn't include the creator's name.", 28 + "minLength": 1, 29 + "maxLength": 2560, 30 + "maxGraphemes": 256 31 + }, 32 + "description": { 33 + "type": "string", 34 + "description": "Description of the track.", 35 + "maxLength": 20000, 36 + "maxGraphemes": 2000 37 + }, 38 + "descriptionFacets": { 39 + "type": "ref", 40 + "description": "Annotations of the track's description.", 41 + "ref": "sh.comet.v0.richtext.facets" 42 + }, 43 + "tags": { 44 + "type": "array", 45 + "description": "Tags/genres for the track. First item is used as the \"primary\" tag. | TODO: reconsider maxLength value", 46 + "minLength": 1, 47 + "maxLength": 5, 48 + "items": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" } 49 + }, 50 + "link": { 51 + "type": "ref", 52 + "ref": "sh.comet.v0.feed.defs#link" 53 + }, 54 + "createdAt": { 55 + "type": "string", 56 + "format": "datetime", 57 + "description": "Timestamp for when the track was originally created/released." 58 + } 59 + } 60 + } 61 + }, 62 + "view": { 63 + "type": "object", 64 + "required": ["uri", "cid", "author", "audio", "record", "indexedAt"], 65 + "properties": { 66 + "uri": { "type": "string", "format": "at-uri" }, 67 + "cid": { "type": "string", "format": "cid" }, 68 + "author": { "type": "ref", "ref": "TODO" }, 69 + "audio": { 70 + "type": "string", 71 + "format": "uri", 72 + "description": "URL pointing to where the audio data for the track can be fetched. May be re-encoded from the original blob." 73 + }, 74 + "image": { 75 + "type": "string", 76 + "format": "uri", 77 + "description": "URL pointing to where the image for the track can be fetched." 78 + }, 79 + "record": { 80 + "type": "ref", 81 + "ref": "#main" 82 + }, 83 + "repostCount": { "type": "integer" }, 84 + "likeCount": { "type": "integer" }, 85 + "playCount": { "type": "integer" }, 86 + "indexedAt": { "type": "string", "format": "datetime" }, 87 + "viewer": { "type": "ref", "ref": "sh.comet.v0.feed.defs#viewerState" } 88 + } 89 + } 90 + } 91 + }
+51
packages/lexicons/sh/comet/v0/richtext/facets.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.comet.v0.richtext.facets", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "description": "Annotation of a sub-string within rich text.", 8 + "required": ["index", "features"], 9 + "properties": { 10 + "index": { "type": "ref", "ref": "#byteSlice" }, 11 + "features": { 12 + "type": "array", 13 + "items": { "type": "union", "refs": ["#mention", "#link", "#tag"] } 14 + } 15 + } 16 + }, 17 + "mention": { 18 + "type": "object", 19 + "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", 20 + "required": ["did"], 21 + "properties": { 22 + "did": { "type": "string", "format": "did" } 23 + } 24 + }, 25 + "link": { 26 + "type": "object", 27 + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 28 + "required": ["uri"], 29 + "properties": { 30 + "uri": { "type": "string", "format": "uri" } 31 + } 32 + }, 33 + "tag": { 34 + "type": "object", 35 + "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').", 36 + "required": ["tag"], 37 + "properties": { 38 + "tag": { "type": "ref", "ref": "sh.comet.v0.feed.defs#tag" } 39 + } 40 + }, 41 + "byteSlice": { 42 + "type": "object", 43 + "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.", 44 + "required": ["byteStart", "byteEnd"], 45 + "properties": { 46 + "byteStart": { "type": "integer", "minimum": 0 }, 47 + "byteEnd": { "type": "integer", "minimum": 0 } 48 + } 49 + } 50 + } 51 + }