WIP. A little custom music server
0
fork

Configure Feed

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

feat: add tracknumber sort, but its buggy

+376 -49
+1 -1
api-testing/get-album-by-id.bru
··· 11 11 } 12 12 13 13 params:path { 14 - id: album_01K97906F8S08EY11STX0DY7K4 14 + id: album_01K9A4A8XP2P471GV74E5N729Q 15 15 } 16 16 17 17 settings {
+1
backend/drizzle/0006_jittery_skreet.sql
··· 1 + ALTER TABLE `song` ADD `trackNumber` integer;
+314
backend/drizzle/meta/0006_snapshot.json
··· 1 + { 2 + "version": "6", 3 + "dialect": "sqlite", 4 + "id": "ccf9852e-d083-4c6c-bb5c-e7be98473aa0", 5 + "prevId": "90ca0009-4544-498c-9ffe-0b014e29e9ff", 6 + "tables": { 7 + "album": { 8 + "name": "album", 9 + "columns": { 10 + "id": { 11 + "name": "id", 12 + "type": "text", 13 + "primaryKey": true, 14 + "notNull": true, 15 + "autoincrement": false 16 + }, 17 + "title": { 18 + "name": "title", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true, 22 + "autoincrement": false 23 + } 24 + }, 25 + "indexes": { 26 + "album_title_unique": { 27 + "name": "album_title_unique", 28 + "columns": [ 29 + "title" 30 + ], 31 + "isUnique": true 32 + } 33 + }, 34 + "foreignKeys": {}, 35 + "compositePrimaryKeys": {}, 36 + "uniqueConstraints": {}, 37 + "checkConstraints": {} 38 + }, 39 + "artist": { 40 + "name": "artist", 41 + "columns": { 42 + "id": { 43 + "name": "id", 44 + "type": "text", 45 + "primaryKey": true, 46 + "notNull": true, 47 + "autoincrement": false 48 + }, 49 + "name": { 50 + "name": "name", 51 + "type": "text", 52 + "primaryKey": false, 53 + "notNull": true, 54 + "autoincrement": false 55 + } 56 + }, 57 + "indexes": { 58 + "artist_name_unique": { 59 + "name": "artist_name_unique", 60 + "columns": [ 61 + "name" 62 + ], 63 + "isUnique": true 64 + } 65 + }, 66 + "foreignKeys": {}, 67 + "compositePrimaryKeys": {}, 68 + "uniqueConstraints": {}, 69 + "checkConstraints": {} 70 + }, 71 + "artist_to_album": { 72 + "name": "artist_to_album", 73 + "columns": { 74 + "artistId": { 75 + "name": "artistId", 76 + "type": "text", 77 + "primaryKey": false, 78 + "notNull": true, 79 + "autoincrement": false 80 + }, 81 + "albumId": { 82 + "name": "albumId", 83 + "type": "text", 84 + "primaryKey": false, 85 + "notNull": true, 86 + "autoincrement": false 87 + } 88 + }, 89 + "indexes": { 90 + "artist_to_album_artistId_albumId_unique": { 91 + "name": "artist_to_album_artistId_albumId_unique", 92 + "columns": [ 93 + "artistId", 94 + "albumId" 95 + ], 96 + "isUnique": true 97 + } 98 + }, 99 + "foreignKeys": { 100 + "artist_to_album_artistId_artist_id_fk": { 101 + "name": "artist_to_album_artistId_artist_id_fk", 102 + "tableFrom": "artist_to_album", 103 + "tableTo": "artist", 104 + "columnsFrom": [ 105 + "artistId" 106 + ], 107 + "columnsTo": [ 108 + "id" 109 + ], 110 + "onDelete": "cascade", 111 + "onUpdate": "no action" 112 + }, 113 + "artist_to_album_albumId_album_id_fk": { 114 + "name": "artist_to_album_albumId_album_id_fk", 115 + "tableFrom": "artist_to_album", 116 + "tableTo": "album", 117 + "columnsFrom": [ 118 + "albumId" 119 + ], 120 + "columnsTo": [ 121 + "id" 122 + ], 123 + "onDelete": "cascade", 124 + "onUpdate": "no action" 125 + } 126 + }, 127 + "compositePrimaryKeys": {}, 128 + "uniqueConstraints": {}, 129 + "checkConstraints": {} 130 + }, 131 + "file": { 132 + "name": "file", 133 + "columns": { 134 + "id": { 135 + "name": "id", 136 + "type": "text", 137 + "primaryKey": true, 138 + "notNull": true, 139 + "autoincrement": false 140 + }, 141 + "path": { 142 + "name": "path", 143 + "type": "text", 144 + "primaryKey": false, 145 + "notNull": true, 146 + "autoincrement": false 147 + } 148 + }, 149 + "indexes": { 150 + "file_path_unique": { 151 + "name": "file_path_unique", 152 + "columns": [ 153 + "path" 154 + ], 155 + "isUnique": true 156 + } 157 + }, 158 + "foreignKeys": {}, 159 + "compositePrimaryKeys": {}, 160 + "uniqueConstraints": {}, 161 + "checkConstraints": {} 162 + }, 163 + "song": { 164 + "name": "song", 165 + "columns": { 166 + "id": { 167 + "name": "id", 168 + "type": "text", 169 + "primaryKey": true, 170 + "notNull": true, 171 + "autoincrement": false 172 + }, 173 + "title": { 174 + "name": "title", 175 + "type": "text", 176 + "primaryKey": false, 177 + "notNull": true, 178 + "autoincrement": false 179 + }, 180 + "trackNumber": { 181 + "name": "trackNumber", 182 + "type": "integer", 183 + "primaryKey": false, 184 + "notNull": false, 185 + "autoincrement": false 186 + }, 187 + "fileId": { 188 + "name": "fileId", 189 + "type": "text", 190 + "primaryKey": false, 191 + "notNull": true, 192 + "autoincrement": false 193 + }, 194 + "albumId": { 195 + "name": "albumId", 196 + "type": "text", 197 + "primaryKey": false, 198 + "notNull": true, 199 + "autoincrement": false 200 + } 201 + }, 202 + "indexes": { 203 + "song_fileId_unique": { 204 + "name": "song_fileId_unique", 205 + "columns": [ 206 + "fileId" 207 + ], 208 + "isUnique": true 209 + } 210 + }, 211 + "foreignKeys": { 212 + "song_fileId_file_id_fk": { 213 + "name": "song_fileId_file_id_fk", 214 + "tableFrom": "song", 215 + "tableTo": "file", 216 + "columnsFrom": [ 217 + "fileId" 218 + ], 219 + "columnsTo": [ 220 + "id" 221 + ], 222 + "onDelete": "cascade", 223 + "onUpdate": "no action" 224 + }, 225 + "song_albumId_album_id_fk": { 226 + "name": "song_albumId_album_id_fk", 227 + "tableFrom": "song", 228 + "tableTo": "album", 229 + "columnsFrom": [ 230 + "albumId" 231 + ], 232 + "columnsTo": [ 233 + "id" 234 + ], 235 + "onDelete": "cascade", 236 + "onUpdate": "no action" 237 + } 238 + }, 239 + "compositePrimaryKeys": {}, 240 + "uniqueConstraints": {}, 241 + "checkConstraints": {} 242 + }, 243 + "song_to_artist": { 244 + "name": "song_to_artist", 245 + "columns": { 246 + "songId": { 247 + "name": "songId", 248 + "type": "text", 249 + "primaryKey": false, 250 + "notNull": true, 251 + "autoincrement": false 252 + }, 253 + "artistId": { 254 + "name": "artistId", 255 + "type": "text", 256 + "primaryKey": false, 257 + "notNull": true, 258 + "autoincrement": false 259 + } 260 + }, 261 + "indexes": { 262 + "song_to_artist_songId_artistId_unique": { 263 + "name": "song_to_artist_songId_artistId_unique", 264 + "columns": [ 265 + "songId", 266 + "artistId" 267 + ], 268 + "isUnique": true 269 + } 270 + }, 271 + "foreignKeys": { 272 + "song_to_artist_songId_song_id_fk": { 273 + "name": "song_to_artist_songId_song_id_fk", 274 + "tableFrom": "song_to_artist", 275 + "tableTo": "song", 276 + "columnsFrom": [ 277 + "songId" 278 + ], 279 + "columnsTo": [ 280 + "id" 281 + ], 282 + "onDelete": "cascade", 283 + "onUpdate": "no action" 284 + }, 285 + "song_to_artist_artistId_artist_id_fk": { 286 + "name": "song_to_artist_artistId_artist_id_fk", 287 + "tableFrom": "song_to_artist", 288 + "tableTo": "artist", 289 + "columnsFrom": [ 290 + "artistId" 291 + ], 292 + "columnsTo": [ 293 + "id" 294 + ], 295 + "onDelete": "cascade", 296 + "onUpdate": "no action" 297 + } 298 + }, 299 + "compositePrimaryKeys": {}, 300 + "uniqueConstraints": {}, 301 + "checkConstraints": {} 302 + } 303 + }, 304 + "views": {}, 305 + "enums": {}, 306 + "_meta": { 307 + "schemas": {}, 308 + "tables": {}, 309 + "columns": {} 310 + }, 311 + "internal": { 312 + "indexes": {} 313 + } 314 + }
+54 -47
backend/drizzle/meta/_journal.json
··· 1 1 { 2 - "version": "7", 3 - "dialect": "sqlite", 4 - "entries": [ 5 - { 6 - "idx": 0, 7 - "version": "6", 8 - "when": 1748676283429, 9 - "tag": "0000_smiling_warlock", 10 - "breakpoints": true 11 - }, 12 - { 13 - "idx": 1, 14 - "version": "6", 15 - "when": 1756567708406, 16 - "tag": "0001_careful_valeria_richards", 17 - "breakpoints": true 18 - }, 19 - { 20 - "idx": 2, 21 - "version": "6", 22 - "when": 1757078489466, 23 - "tag": "0002_curvy_dormammu", 24 - "breakpoints": true 25 - }, 26 - { 27 - "idx": 3, 28 - "version": "6", 29 - "when": 1757101063371, 30 - "tag": "0003_unique_cable", 31 - "breakpoints": true 32 - }, 33 - { 34 - "idx": 4, 35 - "version": "6", 36 - "when": 1757101301885, 37 - "tag": "0004_thankful_meggan", 38 - "breakpoints": true 39 - }, 40 - { 41 - "idx": 5, 42 - "version": "6", 43 - "when": 1757149162775, 44 - "tag": "0005_ambiguous_stryfe", 45 - "breakpoints": true 46 - } 47 - ] 48 - } 2 + "version": "7", 3 + "dialect": "sqlite", 4 + "entries": [ 5 + { 6 + "idx": 0, 7 + "version": "6", 8 + "when": 1748676283429, 9 + "tag": "0000_smiling_warlock", 10 + "breakpoints": true 11 + }, 12 + { 13 + "idx": 1, 14 + "version": "6", 15 + "when": 1756567708406, 16 + "tag": "0001_careful_valeria_richards", 17 + "breakpoints": true 18 + }, 19 + { 20 + "idx": 2, 21 + "version": "6", 22 + "when": 1757078489466, 23 + "tag": "0002_curvy_dormammu", 24 + "breakpoints": true 25 + }, 26 + { 27 + "idx": 3, 28 + "version": "6", 29 + "when": 1757101063371, 30 + "tag": "0003_unique_cable", 31 + "breakpoints": true 32 + }, 33 + { 34 + "idx": 4, 35 + "version": "6", 36 + "when": 1757101301885, 37 + "tag": "0004_thankful_meggan", 38 + "breakpoints": true 39 + }, 40 + { 41 + "idx": 5, 42 + "version": "6", 43 + "when": 1757149162775, 44 + "tag": "0005_ambiguous_stryfe", 45 + "breakpoints": true 46 + }, 47 + { 48 + "idx": 6, 49 + "version": "6", 50 + "when": 1762350364881, 51 + "tag": "0006_jittery_skreet", 52 + "breakpoints": true 53 + } 54 + ] 55 + }
+1
backend/src/api.ts
··· 44 44 }, 45 45 }, 46 46 songs: { 47 + orderBy: [songTable.trackNumber], 47 48 with: { 48 49 artists: { 49 50 with: {
+2 -1
backend/src/db/schema.ts
··· 1 1 import { relations } from "drizzle-orm"; 2 - import { sqliteTable, text, unique } from "drizzle-orm/sqlite-core"; 2 + import { sqliteTable, text, unique, int } from "drizzle-orm/sqlite-core"; 3 3 import { ulid } from "ulid"; 4 4 5 5 const id = (prefix?: string) => ··· 14 14 export const songTable = sqliteTable("song", { 15 15 id: id("song"), 16 16 title: text().notNull(), 17 + trackNumber: int(), 17 18 fileId: text() 18 19 .notNull() 19 20 .references(() => fileTable.id, { onDelete: "cascade" })
+1
backend/src/sync-library.ts
··· 127 127 128 128 const newSongs = Chunk.toArray(chunk).map((x) => ({ 129 129 title: x.title, 130 + trackNumber: x.trackNumber, 130 131 fileId: lookup.files.find((file) => file.filePath === x.filePath)?.id!, 131 132 albumId: lookup.albums.find((album) => album.title === x.album)?.id!, 132 133 }));
+2
backend/src/types.ts
··· 48 48 id: SongId, 49 49 title: Schema.NonEmptyString, 50 50 artists: Schema.Array(Artist), 51 + // TODO: this can only positive non zero int 52 + trackNumber: Schema.NullOr(Schema.Int), 51 53 fileId: AudioFileId, 52 54 }).annotations({ 53 55 title: "songs",