A decentralized music tracking and discovery platform built on AT Protocol 🎵
listenbrainz spotify atproto lastfm musicbrainz scrobbling

Refactor data synchronization logic and repository structure

- Updated the album, artist, track, user, playlist, loved track, and scrobble repositories to include asynchronous data fetching and insertion methods.
- Implemented batch processing for syncing albums, artists, tracks, users, playlists, loved tracks, and scrobbles from a source PostgreSQL database to a destination PostgreSQL database.
- Enhanced error handling during data insertion to log failures without interrupting the sync process.
- Removed unique constraints on albumUri and artistUri fields in the tracks schema to allow for non-unique entries.
- Modified user schema to make displayName optional.
- Updated journal metadata for versioning.

+37 -38
apps/api/drizzle/0000_quiet_mister_sinister.sql apps/api/drizzle/0000_greedy_vanisher.sql
··· 1 1 CREATE TABLE "album_tracks" ( 2 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 2 + "xata_id" text PRIMARY KEY NOT NULL, 3 3 "album_id" text NOT NULL, 4 4 "track_id" text NOT NULL, 5 5 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 8 8 ); 9 9 --> statement-breakpoint 10 10 CREATE TABLE "albums" ( 11 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 11 + "xata_id" text PRIMARY KEY NOT NULL, 12 12 "title" text NOT NULL, 13 13 "artist" text NOT NULL, 14 14 "release_date" text, ··· 33 33 ); 34 34 --> statement-breakpoint 35 35 CREATE TABLE "api_keys" ( 36 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 36 + "xata_id" text PRIMARY KEY NOT NULL, 37 37 "name" text NOT NULL, 38 38 "api_key" text NOT NULL, 39 39 "shared_secret" text NOT NULL, ··· 45 45 ); 46 46 --> statement-breakpoint 47 47 CREATE TABLE "artist_albums" ( 48 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 48 + "xata_id" text PRIMARY KEY NOT NULL, 49 49 "artist_id" text NOT NULL, 50 50 "album_id" text NOT NULL, 51 51 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 54 54 ); 55 55 --> statement-breakpoint 56 56 CREATE TABLE "artist_tracks" ( 57 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 57 + "xata_id" text PRIMARY KEY NOT NULL, 58 58 "artist_id" text NOT NULL, 59 59 "track_id" text NOT NULL, 60 60 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 63 63 ); 64 64 --> statement-breakpoint 65 65 CREATE TABLE "artists" ( 66 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 66 + "xata_id" text PRIMARY KEY NOT NULL, 67 67 "name" text NOT NULL, 68 68 "biography" text, 69 69 "born" timestamp, ··· 76 76 "spotify_link" text, 77 77 "tidal_link" text, 78 78 "youtube_link" text, 79 + "genres" text[], 79 80 "xata_createdat" timestamp DEFAULT now() NOT NULL, 80 81 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 81 82 "xata_version" integer, ··· 84 85 ); 85 86 --> statement-breakpoint 86 87 CREATE TABLE "dropbox_accounts" ( 87 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 88 + "xata_id" text PRIMARY KEY NOT NULL, 88 89 "email" text NOT NULL, 89 90 "is_beta_user" boolean DEFAULT false NOT NULL, 90 91 "user_id" text NOT NULL, ··· 95 96 ); 96 97 --> statement-breakpoint 97 98 CREATE TABLE "dropbox_directories" ( 98 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 99 + "xata_id" text PRIMARY KEY NOT NULL, 99 100 "name" text NOT NULL, 100 101 "path" text NOT NULL, 101 102 "parent_id" text, ··· 108 109 ); 109 110 --> statement-breakpoint 110 111 CREATE TABLE "dropbox_paths" ( 111 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 112 + "xata_id" text PRIMARY KEY NOT NULL, 112 113 "path" text NOT NULL, 113 114 "name" text NOT NULL, 114 115 "dropbox_id" text NOT NULL, ··· 122 123 ); 123 124 --> statement-breakpoint 124 125 CREATE TABLE "dropbox_tokens" ( 125 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 126 + "xata_id" text PRIMARY KEY NOT NULL, 126 127 "refresh_token" text NOT NULL, 127 128 "xata_createdat" timestamp DEFAULT now() NOT NULL, 128 129 "xata_updatedat" timestamp DEFAULT now() NOT NULL 129 130 ); 130 131 --> statement-breakpoint 131 132 CREATE TABLE "dropbox" ( 132 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 133 + "xata_id" text PRIMARY KEY NOT NULL, 133 134 "user_id" text NOT NULL, 134 135 "dropbox_token_id" text NOT NULL, 135 136 "xata_version" text NOT NULL, ··· 138 139 ); 139 140 --> statement-breakpoint 140 141 CREATE TABLE "google_drive_accounts" ( 141 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 142 + "xata_id" text PRIMARY KEY NOT NULL, 142 143 "email" text NOT NULL, 143 144 "is_beta_user" boolean DEFAULT false NOT NULL, 144 145 "user_id" text NOT NULL, ··· 149 150 ); 150 151 --> statement-breakpoint 151 152 CREATE TABLE "google_drive_directories" ( 152 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 153 + "xata_id" text PRIMARY KEY NOT NULL, 153 154 "name" text NOT NULL, 154 155 "path" text NOT NULL, 155 156 "parent_id" text, ··· 162 163 ); 163 164 --> statement-breakpoint 164 165 CREATE TABLE "google_drive_paths" ( 165 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 166 + "xata_id" text PRIMARY KEY NOT NULL, 166 167 "google_drive_id" text NOT NULL, 167 168 "track_id" text NOT NULL, 168 169 "name" text NOT NULL, ··· 175 176 ); 176 177 --> statement-breakpoint 177 178 CREATE TABLE "google_drive_tokens" ( 178 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 179 + "xata_id" text PRIMARY KEY NOT NULL, 179 180 "refresh_token" text NOT NULL, 180 181 "xata_createdat" timestamp DEFAULT now() NOT NULL, 181 182 "xata_updatedat" timestamp DEFAULT now() NOT NULL 182 183 ); 183 184 --> statement-breakpoint 184 185 CREATE TABLE "google_drive" ( 185 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 186 + "xata_id" text PRIMARY KEY NOT NULL, 186 187 "google_drive_token_id" text NOT NULL, 187 188 "user_id" text NOT NULL, 188 189 "xata_version" text NOT NULL, ··· 191 192 ); 192 193 --> statement-breakpoint 193 194 CREATE TABLE "loved_tracks" ( 194 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 195 + "xata_id" text PRIMARY KEY NOT NULL, 195 196 "user_id" text NOT NULL, 196 197 "track_id" text NOT NULL, 197 198 "uri" text, ··· 200 201 ); 201 202 --> statement-breakpoint 202 203 CREATE TABLE "playlist_tracks" ( 203 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 204 + "xata_id" text PRIMARY KEY NOT NULL, 204 205 "playlist_id" text NOT NULL, 205 206 "track_id" text NOT NULL, 206 207 "xata_createdat" timestamp DEFAULT now() NOT NULL 207 208 ); 208 209 --> statement-breakpoint 209 210 CREATE TABLE "playlists" ( 210 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 211 + "xata_id" text PRIMARY KEY NOT NULL, 211 212 "name" text NOT NULL, 212 213 "picture" text, 213 214 "description" text, ··· 222 223 ); 223 224 --> statement-breakpoint 224 225 CREATE TABLE "profile_shouts" ( 225 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 226 + "xata_id" text PRIMARY KEY NOT NULL, 226 227 "user_id" text NOT NULL, 227 228 "shout_id" text NOT NULL, 228 229 "xata_createdat" timestamp DEFAULT now() NOT NULL 229 230 ); 230 231 --> statement-breakpoint 231 232 CREATE TABLE "queue_tracks" ( 232 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 233 + "xata_id" text PRIMARY KEY NOT NULL, 233 234 "user_id" text NOT NULL, 234 235 "track_id" text NOT NULL, 235 236 "position" integer NOT NULL, ··· 240 241 ); 241 242 --> statement-breakpoint 242 243 CREATE TABLE "scrobbles" ( 243 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 244 + "xata_id" text PRIMARY KEY NOT NULL, 244 245 "user_id" text, 245 246 "track_id" text, 246 247 "album_id" text, ··· 254 255 ); 255 256 --> statement-breakpoint 256 257 CREATE TABLE "shout_likes" ( 257 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 258 + "xata_id" text PRIMARY KEY NOT NULL, 258 259 "user_id" text NOT NULL, 259 260 "shout_id" text NOT NULL, 260 261 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 263 264 ); 264 265 --> statement-breakpoint 265 266 CREATE TABLE "shout_reports" ( 266 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 267 + "xata_id" text PRIMARY KEY NOT NULL, 267 268 "user_id" text NOT NULL, 268 269 "shout_id" text NOT NULL, 269 270 "xata_createdat" timestamp DEFAULT now() NOT NULL 270 271 ); 271 272 --> statement-breakpoint 272 273 CREATE TABLE "shouts" ( 273 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 274 + "xata_id" text PRIMARY KEY NOT NULL, 274 275 "content" text NOT NULL, 275 276 "track_id" text, 276 277 "artist_id" text, ··· 285 286 ); 286 287 --> statement-breakpoint 287 288 CREATE TABLE "spotify_accounts" ( 288 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 289 + "xata_id" text PRIMARY KEY NOT NULL, 289 290 "xata_version" integer NOT NULL, 290 291 "email" text NOT NULL, 291 292 "user_id" text NOT NULL, ··· 295 296 ); 296 297 --> statement-breakpoint 297 298 CREATE TABLE "spotify_tokens" ( 298 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 299 + "xata_id" text PRIMARY KEY NOT NULL, 299 300 "xata_version" integer NOT NULL, 300 301 "access_token" text NOT NULL, 301 302 "refresh_token" text NOT NULL, ··· 305 306 ); 306 307 --> statement-breakpoint 307 308 CREATE TABLE "tracks" ( 308 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 309 + "xata_id" text PRIMARY KEY NOT NULL, 309 310 "title" text NOT NULL, 310 311 "artist" text NOT NULL, 311 312 "album_artist" text NOT NULL, ··· 337 338 CONSTRAINT "tracks_apple_music_link_unique" UNIQUE("apple_music_link"), 338 339 CONSTRAINT "tracks_tidal_link_unique" UNIQUE("tidal_link"), 339 340 CONSTRAINT "tracks_sha256_unique" UNIQUE("sha256"), 340 - CONSTRAINT "tracks_uri_unique" UNIQUE("uri"), 341 - CONSTRAINT "tracks_album_uri_unique" UNIQUE("album_uri"), 342 - CONSTRAINT "tracks_artist_uri_unique" UNIQUE("artist_uri") 341 + CONSTRAINT "tracks_uri_unique" UNIQUE("uri") 343 342 ); 344 343 --> statement-breakpoint 345 344 CREATE TABLE "user_albums" ( 346 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 345 + "xata_id" text PRIMARY KEY NOT NULL, 347 346 "user_id" text NOT NULL, 348 347 "album_id" text NOT NULL, 349 348 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 355 354 ); 356 355 --> statement-breakpoint 357 356 CREATE TABLE "user_artists" ( 358 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 357 + "xata_id" text PRIMARY KEY NOT NULL, 359 358 "user_id" text NOT NULL, 360 359 "artist_id" text NOT NULL, 361 360 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 367 366 ); 368 367 --> statement-breakpoint 369 368 CREATE TABLE "user_playlists" ( 370 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 369 + "xata_id" text PRIMARY KEY NOT NULL, 371 370 "user_id" text NOT NULL, 372 371 "playlist_id" text NOT NULL, 373 372 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 376 375 ); 377 376 --> statement-breakpoint 378 377 CREATE TABLE "user_tracks" ( 379 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 378 + "xata_id" text PRIMARY KEY NOT NULL, 380 379 "user_id" text NOT NULL, 381 380 "track_id" text NOT NULL, 382 381 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 388 387 ); 389 388 --> statement-breakpoint 390 389 CREATE TABLE "users" ( 391 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 390 + "xata_id" text PRIMARY KEY NOT NULL, 392 391 "did" text NOT NULL, 393 - "display_name" text NOT NULL, 392 + "display_name" text, 394 393 "handle" text NOT NULL, 395 394 "avatar" text NOT NULL, 396 395 "xata_createdat" timestamp DEFAULT now() NOT NULL, ··· 401 400 ); 402 401 --> statement-breakpoint 403 402 CREATE TABLE "webscrobblers" ( 404 - "xata_id" text PRIMARY KEY DEFAULT xata_id(), 403 + "xata_id" text PRIMARY KEY NOT NULL, 405 404 "name" text NOT NULL, 406 405 "uuid" text NOT NULL, 407 406 "description" text,
-1
apps/api/drizzle/0001_woozy_saracen.sql
··· 1 - ALTER TABLE "artists" ADD COLUMN "genres" text[];
+8 -16
apps/api/drizzle/meta/0000_snapshot.json
··· 1 1 { 2 - "id": "49adc24b-83c8-4065-8e13-80045e7f0f27", 2 + "id": "97b0b357-40d7-42b5-ab53-2738bd804a2c", 3 3 "prevId": "00000000-0000-0000-0000-000000000000", 4 4 "version": "7", 5 5 "dialect": "postgresql", ··· 555 555 "youtube_link": { 556 556 "name": "youtube_link", 557 557 "type": "text", 558 + "primaryKey": false, 559 + "notNull": false 560 + }, 561 + "genres": { 562 + "name": "genres", 563 + "type": "text[]", 558 564 "primaryKey": false, 559 565 "notNull": false 560 566 }, ··· 2576 2582 "columns": [ 2577 2583 "uri" 2578 2584 ] 2579 - }, 2580 - "tracks_album_uri_unique": { 2581 - "name": "tracks_album_uri_unique", 2582 - "nullsNotDistinct": false, 2583 - "columns": [ 2584 - "album_uri" 2585 - ] 2586 - }, 2587 - "tracks_artist_uri_unique": { 2588 - "name": "tracks_artist_uri_unique", 2589 - "nullsNotDistinct": false, 2590 - "columns": [ 2591 - "artist_uri" 2592 - ] 2593 2585 } 2594 2586 }, 2595 2587 "policies": {}, ··· 2989 2981 "name": "display_name", 2990 2982 "type": "text", 2991 2983 "primaryKey": false, 2992 - "notNull": true 2984 + "notNull": false 2993 2985 }, 2994 2986 "handle": { 2995 2987 "name": "handle",
-3146
apps/api/drizzle/meta/0001_snapshot.json
··· 1 - { 2 - "id": "aeb183b0-c260-41fa-959b-2cde9fced54e", 3 - "prevId": "49adc24b-83c8-4065-8e13-80045e7f0f27", 4 - "version": "7", 5 - "dialect": "postgresql", 6 - "tables": { 7 - "public.album_tracks": { 8 - "name": "album_tracks", 9 - "schema": "", 10 - "columns": { 11 - "xata_id": { 12 - "name": "xata_id", 13 - "type": "text", 14 - "primaryKey": true, 15 - "notNull": true 16 - }, 17 - "album_id": { 18 - "name": "album_id", 19 - "type": "text", 20 - "primaryKey": false, 21 - "notNull": true 22 - }, 23 - "track_id": { 24 - "name": "track_id", 25 - "type": "text", 26 - "primaryKey": false, 27 - "notNull": true 28 - }, 29 - "xata_createdat": { 30 - "name": "xata_createdat", 31 - "type": "timestamp", 32 - "primaryKey": false, 33 - "notNull": true, 34 - "default": "now()" 35 - }, 36 - "xata_updatedat": { 37 - "name": "xata_updatedat", 38 - "type": "timestamp", 39 - "primaryKey": false, 40 - "notNull": true, 41 - "default": "now()" 42 - }, 43 - "xata_version": { 44 - "name": "xata_version", 45 - "type": "integer", 46 - "primaryKey": false, 47 - "notNull": true 48 - } 49 - }, 50 - "indexes": {}, 51 - "foreignKeys": { 52 - "album_tracks_album_id_albums_xata_id_fk": { 53 - "name": "album_tracks_album_id_albums_xata_id_fk", 54 - "tableFrom": "album_tracks", 55 - "tableTo": "albums", 56 - "columnsFrom": [ 57 - "album_id" 58 - ], 59 - "columnsTo": [ 60 - "xata_id" 61 - ], 62 - "onDelete": "no action", 63 - "onUpdate": "no action" 64 - }, 65 - "album_tracks_track_id_tracks_xata_id_fk": { 66 - "name": "album_tracks_track_id_tracks_xata_id_fk", 67 - "tableFrom": "album_tracks", 68 - "tableTo": "tracks", 69 - "columnsFrom": [ 70 - "track_id" 71 - ], 72 - "columnsTo": [ 73 - "xata_id" 74 - ], 75 - "onDelete": "no action", 76 - "onUpdate": "no action" 77 - } 78 - }, 79 - "compositePrimaryKeys": {}, 80 - "uniqueConstraints": {}, 81 - "policies": {}, 82 - "checkConstraints": {}, 83 - "isRLSEnabled": false 84 - }, 85 - "public.albums": { 86 - "name": "albums", 87 - "schema": "", 88 - "columns": { 89 - "xata_id": { 90 - "name": "xata_id", 91 - "type": "text", 92 - "primaryKey": true, 93 - "notNull": true 94 - }, 95 - "title": { 96 - "name": "title", 97 - "type": "text", 98 - "primaryKey": false, 99 - "notNull": true 100 - }, 101 - "artist": { 102 - "name": "artist", 103 - "type": "text", 104 - "primaryKey": false, 105 - "notNull": true 106 - }, 107 - "release_date": { 108 - "name": "release_date", 109 - "type": "text", 110 - "primaryKey": false, 111 - "notNull": false 112 - }, 113 - "year": { 114 - "name": "year", 115 - "type": "integer", 116 - "primaryKey": false, 117 - "notNull": false 118 - }, 119 - "album_art": { 120 - "name": "album_art", 121 - "type": "text", 122 - "primaryKey": false, 123 - "notNull": false 124 - }, 125 - "uri": { 126 - "name": "uri", 127 - "type": "text", 128 - "primaryKey": false, 129 - "notNull": false 130 - }, 131 - "artist_uri": { 132 - "name": "artist_uri", 133 - "type": "text", 134 - "primaryKey": false, 135 - "notNull": false 136 - }, 137 - "apple_music_link": { 138 - "name": "apple_music_link", 139 - "type": "text", 140 - "primaryKey": false, 141 - "notNull": false 142 - }, 143 - "spotify_link": { 144 - "name": "spotify_link", 145 - "type": "text", 146 - "primaryKey": false, 147 - "notNull": false 148 - }, 149 - "tidal_link": { 150 - "name": "tidal_link", 151 - "type": "text", 152 - "primaryKey": false, 153 - "notNull": false 154 - }, 155 - "youtube_link": { 156 - "name": "youtube_link", 157 - "type": "text", 158 - "primaryKey": false, 159 - "notNull": false 160 - }, 161 - "sha256": { 162 - "name": "sha256", 163 - "type": "text", 164 - "primaryKey": false, 165 - "notNull": true 166 - }, 167 - "xata_createdat": { 168 - "name": "xata_createdat", 169 - "type": "timestamp", 170 - "primaryKey": false, 171 - "notNull": true, 172 - "default": "now()" 173 - }, 174 - "xata_updatedat": { 175 - "name": "xata_updatedat", 176 - "type": "timestamp", 177 - "primaryKey": false, 178 - "notNull": true, 179 - "default": "now()" 180 - }, 181 - "xata_version": { 182 - "name": "xata_version", 183 - "type": "integer", 184 - "primaryKey": false, 185 - "notNull": false 186 - } 187 - }, 188 - "indexes": {}, 189 - "foreignKeys": {}, 190 - "compositePrimaryKeys": {}, 191 - "uniqueConstraints": { 192 - "albums_uri_unique": { 193 - "name": "albums_uri_unique", 194 - "nullsNotDistinct": false, 195 - "columns": [ 196 - "uri" 197 - ] 198 - }, 199 - "albums_apple_music_link_unique": { 200 - "name": "albums_apple_music_link_unique", 201 - "nullsNotDistinct": false, 202 - "columns": [ 203 - "apple_music_link" 204 - ] 205 - }, 206 - "albums_spotify_link_unique": { 207 - "name": "albums_spotify_link_unique", 208 - "nullsNotDistinct": false, 209 - "columns": [ 210 - "spotify_link" 211 - ] 212 - }, 213 - "albums_tidal_link_unique": { 214 - "name": "albums_tidal_link_unique", 215 - "nullsNotDistinct": false, 216 - "columns": [ 217 - "tidal_link" 218 - ] 219 - }, 220 - "albums_youtube_link_unique": { 221 - "name": "albums_youtube_link_unique", 222 - "nullsNotDistinct": false, 223 - "columns": [ 224 - "youtube_link" 225 - ] 226 - }, 227 - "albums_sha256_unique": { 228 - "name": "albums_sha256_unique", 229 - "nullsNotDistinct": false, 230 - "columns": [ 231 - "sha256" 232 - ] 233 - } 234 - }, 235 - "policies": {}, 236 - "checkConstraints": {}, 237 - "isRLSEnabled": false 238 - }, 239 - "public.api_keys": { 240 - "name": "api_keys", 241 - "schema": "", 242 - "columns": { 243 - "xata_id": { 244 - "name": "xata_id", 245 - "type": "text", 246 - "primaryKey": true, 247 - "notNull": true 248 - }, 249 - "name": { 250 - "name": "name", 251 - "type": "text", 252 - "primaryKey": false, 253 - "notNull": true 254 - }, 255 - "api_key": { 256 - "name": "api_key", 257 - "type": "text", 258 - "primaryKey": false, 259 - "notNull": true 260 - }, 261 - "shared_secret": { 262 - "name": "shared_secret", 263 - "type": "text", 264 - "primaryKey": false, 265 - "notNull": true 266 - }, 267 - "description": { 268 - "name": "description", 269 - "type": "text", 270 - "primaryKey": false, 271 - "notNull": false 272 - }, 273 - "enabled": { 274 - "name": "enabled", 275 - "type": "boolean", 276 - "primaryKey": false, 277 - "notNull": true, 278 - "default": true 279 - }, 280 - "user_id": { 281 - "name": "user_id", 282 - "type": "text", 283 - "primaryKey": false, 284 - "notNull": true 285 - }, 286 - "xata_createdat": { 287 - "name": "xata_createdat", 288 - "type": "timestamp", 289 - "primaryKey": false, 290 - "notNull": true, 291 - "default": "now()" 292 - }, 293 - "xata_updatedat": { 294 - "name": "xata_updatedat", 295 - "type": "timestamp", 296 - "primaryKey": false, 297 - "notNull": true, 298 - "default": "now()" 299 - } 300 - }, 301 - "indexes": {}, 302 - "foreignKeys": { 303 - "api_keys_user_id_users_xata_id_fk": { 304 - "name": "api_keys_user_id_users_xata_id_fk", 305 - "tableFrom": "api_keys", 306 - "tableTo": "users", 307 - "columnsFrom": [ 308 - "user_id" 309 - ], 310 - "columnsTo": [ 311 - "xata_id" 312 - ], 313 - "onDelete": "no action", 314 - "onUpdate": "no action" 315 - } 316 - }, 317 - "compositePrimaryKeys": {}, 318 - "uniqueConstraints": {}, 319 - "policies": {}, 320 - "checkConstraints": {}, 321 - "isRLSEnabled": false 322 - }, 323 - "public.artist_albums": { 324 - "name": "artist_albums", 325 - "schema": "", 326 - "columns": { 327 - "xata_id": { 328 - "name": "xata_id", 329 - "type": "text", 330 - "primaryKey": true, 331 - "notNull": true 332 - }, 333 - "artist_id": { 334 - "name": "artist_id", 335 - "type": "text", 336 - "primaryKey": false, 337 - "notNull": true 338 - }, 339 - "album_id": { 340 - "name": "album_id", 341 - "type": "text", 342 - "primaryKey": false, 343 - "notNull": true 344 - }, 345 - "xata_createdat": { 346 - "name": "xata_createdat", 347 - "type": "timestamp", 348 - "primaryKey": false, 349 - "notNull": true, 350 - "default": "now()" 351 - }, 352 - "xata_updatedat": { 353 - "name": "xata_updatedat", 354 - "type": "timestamp", 355 - "primaryKey": false, 356 - "notNull": true, 357 - "default": "now()" 358 - }, 359 - "xata_version": { 360 - "name": "xata_version", 361 - "type": "integer", 362 - "primaryKey": false, 363 - "notNull": true 364 - } 365 - }, 366 - "indexes": {}, 367 - "foreignKeys": { 368 - "artist_albums_artist_id_artists_xata_id_fk": { 369 - "name": "artist_albums_artist_id_artists_xata_id_fk", 370 - "tableFrom": "artist_albums", 371 - "tableTo": "artists", 372 - "columnsFrom": [ 373 - "artist_id" 374 - ], 375 - "columnsTo": [ 376 - "xata_id" 377 - ], 378 - "onDelete": "no action", 379 - "onUpdate": "no action" 380 - }, 381 - "artist_albums_album_id_albums_xata_id_fk": { 382 - "name": "artist_albums_album_id_albums_xata_id_fk", 383 - "tableFrom": "artist_albums", 384 - "tableTo": "albums", 385 - "columnsFrom": [ 386 - "album_id" 387 - ], 388 - "columnsTo": [ 389 - "xata_id" 390 - ], 391 - "onDelete": "no action", 392 - "onUpdate": "no action" 393 - } 394 - }, 395 - "compositePrimaryKeys": {}, 396 - "uniqueConstraints": {}, 397 - "policies": {}, 398 - "checkConstraints": {}, 399 - "isRLSEnabled": false 400 - }, 401 - "public.artist_tracks": { 402 - "name": "artist_tracks", 403 - "schema": "", 404 - "columns": { 405 - "xata_id": { 406 - "name": "xata_id", 407 - "type": "text", 408 - "primaryKey": true, 409 - "notNull": true 410 - }, 411 - "artist_id": { 412 - "name": "artist_id", 413 - "type": "text", 414 - "primaryKey": false, 415 - "notNull": true 416 - }, 417 - "track_id": { 418 - "name": "track_id", 419 - "type": "text", 420 - "primaryKey": false, 421 - "notNull": true 422 - }, 423 - "xata_createdat": { 424 - "name": "xata_createdat", 425 - "type": "timestamp", 426 - "primaryKey": false, 427 - "notNull": true, 428 - "default": "now()" 429 - }, 430 - "xata_updatedat": { 431 - "name": "xata_updatedat", 432 - "type": "timestamp", 433 - "primaryKey": false, 434 - "notNull": true, 435 - "default": "now()" 436 - }, 437 - "xata_version": { 438 - "name": "xata_version", 439 - "type": "integer", 440 - "primaryKey": false, 441 - "notNull": true 442 - } 443 - }, 444 - "indexes": {}, 445 - "foreignKeys": { 446 - "artist_tracks_artist_id_artists_xata_id_fk": { 447 - "name": "artist_tracks_artist_id_artists_xata_id_fk", 448 - "tableFrom": "artist_tracks", 449 - "tableTo": "artists", 450 - "columnsFrom": [ 451 - "artist_id" 452 - ], 453 - "columnsTo": [ 454 - "xata_id" 455 - ], 456 - "onDelete": "no action", 457 - "onUpdate": "no action" 458 - }, 459 - "artist_tracks_track_id_tracks_xata_id_fk": { 460 - "name": "artist_tracks_track_id_tracks_xata_id_fk", 461 - "tableFrom": "artist_tracks", 462 - "tableTo": "tracks", 463 - "columnsFrom": [ 464 - "track_id" 465 - ], 466 - "columnsTo": [ 467 - "xata_id" 468 - ], 469 - "onDelete": "no action", 470 - "onUpdate": "no action" 471 - } 472 - }, 473 - "compositePrimaryKeys": {}, 474 - "uniqueConstraints": {}, 475 - "policies": {}, 476 - "checkConstraints": {}, 477 - "isRLSEnabled": false 478 - }, 479 - "public.artists": { 480 - "name": "artists", 481 - "schema": "", 482 - "columns": { 483 - "xata_id": { 484 - "name": "xata_id", 485 - "type": "text", 486 - "primaryKey": true, 487 - "notNull": true 488 - }, 489 - "name": { 490 - "name": "name", 491 - "type": "text", 492 - "primaryKey": false, 493 - "notNull": true 494 - }, 495 - "biography": { 496 - "name": "biography", 497 - "type": "text", 498 - "primaryKey": false, 499 - "notNull": false 500 - }, 501 - "born": { 502 - "name": "born", 503 - "type": "timestamp", 504 - "primaryKey": false, 505 - "notNull": false 506 - }, 507 - "born_in": { 508 - "name": "born_in", 509 - "type": "text", 510 - "primaryKey": false, 511 - "notNull": false 512 - }, 513 - "died": { 514 - "name": "died", 515 - "type": "timestamp", 516 - "primaryKey": false, 517 - "notNull": false 518 - }, 519 - "picture": { 520 - "name": "picture", 521 - "type": "text", 522 - "primaryKey": false, 523 - "notNull": false 524 - }, 525 - "sha256": { 526 - "name": "sha256", 527 - "type": "text", 528 - "primaryKey": false, 529 - "notNull": true 530 - }, 531 - "uri": { 532 - "name": "uri", 533 - "type": "text", 534 - "primaryKey": false, 535 - "notNull": false 536 - }, 537 - "apple_music_link": { 538 - "name": "apple_music_link", 539 - "type": "text", 540 - "primaryKey": false, 541 - "notNull": false 542 - }, 543 - "spotify_link": { 544 - "name": "spotify_link", 545 - "type": "text", 546 - "primaryKey": false, 547 - "notNull": false 548 - }, 549 - "tidal_link": { 550 - "name": "tidal_link", 551 - "type": "text", 552 - "primaryKey": false, 553 - "notNull": false 554 - }, 555 - "youtube_link": { 556 - "name": "youtube_link", 557 - "type": "text", 558 - "primaryKey": false, 559 - "notNull": false 560 - }, 561 - "genres": { 562 - "name": "genres", 563 - "type": "text[]", 564 - "primaryKey": false, 565 - "notNull": false 566 - }, 567 - "xata_createdat": { 568 - "name": "xata_createdat", 569 - "type": "timestamp", 570 - "primaryKey": false, 571 - "notNull": true, 572 - "default": "now()" 573 - }, 574 - "xata_updatedat": { 575 - "name": "xata_updatedat", 576 - "type": "timestamp", 577 - "primaryKey": false, 578 - "notNull": true, 579 - "default": "now()" 580 - }, 581 - "xata_version": { 582 - "name": "xata_version", 583 - "type": "integer", 584 - "primaryKey": false, 585 - "notNull": false 586 - } 587 - }, 588 - "indexes": {}, 589 - "foreignKeys": {}, 590 - "compositePrimaryKeys": {}, 591 - "uniqueConstraints": { 592 - "artists_sha256_unique": { 593 - "name": "artists_sha256_unique", 594 - "nullsNotDistinct": false, 595 - "columns": [ 596 - "sha256" 597 - ] 598 - }, 599 - "artists_uri_unique": { 600 - "name": "artists_uri_unique", 601 - "nullsNotDistinct": false, 602 - "columns": [ 603 - "uri" 604 - ] 605 - } 606 - }, 607 - "policies": {}, 608 - "checkConstraints": {}, 609 - "isRLSEnabled": false 610 - }, 611 - "public.dropbox_accounts": { 612 - "name": "dropbox_accounts", 613 - "schema": "", 614 - "columns": { 615 - "xata_id": { 616 - "name": "xata_id", 617 - "type": "text", 618 - "primaryKey": true, 619 - "notNull": true 620 - }, 621 - "email": { 622 - "name": "email", 623 - "type": "text", 624 - "primaryKey": false, 625 - "notNull": true 626 - }, 627 - "is_beta_user": { 628 - "name": "is_beta_user", 629 - "type": "boolean", 630 - "primaryKey": false, 631 - "notNull": true, 632 - "default": false 633 - }, 634 - "user_id": { 635 - "name": "user_id", 636 - "type": "text", 637 - "primaryKey": false, 638 - "notNull": true 639 - }, 640 - "xata_version": { 641 - "name": "xata_version", 642 - "type": "text", 643 - "primaryKey": false, 644 - "notNull": true 645 - }, 646 - "xata_createdat": { 647 - "name": "xata_createdat", 648 - "type": "timestamp", 649 - "primaryKey": false, 650 - "notNull": true, 651 - "default": "now()" 652 - }, 653 - "xata_updatedat": { 654 - "name": "xata_updatedat", 655 - "type": "timestamp", 656 - "primaryKey": false, 657 - "notNull": true, 658 - "default": "now()" 659 - } 660 - }, 661 - "indexes": {}, 662 - "foreignKeys": { 663 - "dropbox_accounts_user_id_users_xata_id_fk": { 664 - "name": "dropbox_accounts_user_id_users_xata_id_fk", 665 - "tableFrom": "dropbox_accounts", 666 - "tableTo": "users", 667 - "columnsFrom": [ 668 - "user_id" 669 - ], 670 - "columnsTo": [ 671 - "xata_id" 672 - ], 673 - "onDelete": "no action", 674 - "onUpdate": "no action" 675 - } 676 - }, 677 - "compositePrimaryKeys": {}, 678 - "uniqueConstraints": { 679 - "dropbox_accounts_email_unique": { 680 - "name": "dropbox_accounts_email_unique", 681 - "nullsNotDistinct": false, 682 - "columns": [ 683 - "email" 684 - ] 685 - } 686 - }, 687 - "policies": {}, 688 - "checkConstraints": {}, 689 - "isRLSEnabled": false 690 - }, 691 - "public.dropbox_directories": { 692 - "name": "dropbox_directories", 693 - "schema": "", 694 - "columns": { 695 - "xata_id": { 696 - "name": "xata_id", 697 - "type": "text", 698 - "primaryKey": true, 699 - "notNull": true 700 - }, 701 - "name": { 702 - "name": "name", 703 - "type": "text", 704 - "primaryKey": false, 705 - "notNull": true 706 - }, 707 - "path": { 708 - "name": "path", 709 - "type": "text", 710 - "primaryKey": false, 711 - "notNull": true 712 - }, 713 - "parent_id": { 714 - "name": "parent_id", 715 - "type": "text", 716 - "primaryKey": false, 717 - "notNull": false 718 - }, 719 - "dropbox_id": { 720 - "name": "dropbox_id", 721 - "type": "text", 722 - "primaryKey": false, 723 - "notNull": true 724 - }, 725 - "file_id": { 726 - "name": "file_id", 727 - "type": "text", 728 - "primaryKey": false, 729 - "notNull": true 730 - }, 731 - "xata_version": { 732 - "name": "xata_version", 733 - "type": "text", 734 - "primaryKey": false, 735 - "notNull": true 736 - }, 737 - "xata_createdat": { 738 - "name": "xata_createdat", 739 - "type": "timestamp", 740 - "primaryKey": false, 741 - "notNull": true, 742 - "default": "now()" 743 - }, 744 - "xata_updatedat": { 745 - "name": "xata_updatedat", 746 - "type": "timestamp", 747 - "primaryKey": false, 748 - "notNull": true, 749 - "default": "now()" 750 - } 751 - }, 752 - "indexes": {}, 753 - "foreignKeys": { 754 - "dropbox_directories_parent_id_dropbox_directories_xata_id_fk": { 755 - "name": "dropbox_directories_parent_id_dropbox_directories_xata_id_fk", 756 - "tableFrom": "dropbox_directories", 757 - "tableTo": "dropbox_directories", 758 - "columnsFrom": [ 759 - "parent_id" 760 - ], 761 - "columnsTo": [ 762 - "xata_id" 763 - ], 764 - "onDelete": "no action", 765 - "onUpdate": "no action" 766 - } 767 - }, 768 - "compositePrimaryKeys": {}, 769 - "uniqueConstraints": { 770 - "dropbox_directories_file_id_unique": { 771 - "name": "dropbox_directories_file_id_unique", 772 - "nullsNotDistinct": false, 773 - "columns": [ 774 - "file_id" 775 - ] 776 - } 777 - }, 778 - "policies": {}, 779 - "checkConstraints": {}, 780 - "isRLSEnabled": false 781 - }, 782 - "public.dropbox_paths": { 783 - "name": "dropbox_paths", 784 - "schema": "", 785 - "columns": { 786 - "xata_id": { 787 - "name": "xata_id", 788 - "type": "text", 789 - "primaryKey": true, 790 - "notNull": true 791 - }, 792 - "path": { 793 - "name": "path", 794 - "type": "text", 795 - "primaryKey": false, 796 - "notNull": true 797 - }, 798 - "name": { 799 - "name": "name", 800 - "type": "text", 801 - "primaryKey": false, 802 - "notNull": true 803 - }, 804 - "dropbox_id": { 805 - "name": "dropbox_id", 806 - "type": "text", 807 - "primaryKey": false, 808 - "notNull": true 809 - }, 810 - "track_id": { 811 - "name": "track_id", 812 - "type": "text", 813 - "primaryKey": false, 814 - "notNull": true 815 - }, 816 - "directory_id": { 817 - "name": "directory_id", 818 - "type": "text", 819 - "primaryKey": false, 820 - "notNull": false 821 - }, 822 - "file_id": { 823 - "name": "file_id", 824 - "type": "text", 825 - "primaryKey": false, 826 - "notNull": true 827 - }, 828 - "xata_version": { 829 - "name": "xata_version", 830 - "type": "text", 831 - "primaryKey": false, 832 - "notNull": true 833 - }, 834 - "xata_createdat": { 835 - "name": "xata_createdat", 836 - "type": "timestamp", 837 - "primaryKey": false, 838 - "notNull": true, 839 - "default": "now()" 840 - }, 841 - "xata_updatedat": { 842 - "name": "xata_updatedat", 843 - "type": "timestamp", 844 - "primaryKey": false, 845 - "notNull": true, 846 - "default": "now()" 847 - } 848 - }, 849 - "indexes": {}, 850 - "foreignKeys": { 851 - "dropbox_paths_directory_id_dropbox_directories_xata_id_fk": { 852 - "name": "dropbox_paths_directory_id_dropbox_directories_xata_id_fk", 853 - "tableFrom": "dropbox_paths", 854 - "tableTo": "dropbox_directories", 855 - "columnsFrom": [ 856 - "directory_id" 857 - ], 858 - "columnsTo": [ 859 - "xata_id" 860 - ], 861 - "onDelete": "no action", 862 - "onUpdate": "no action" 863 - } 864 - }, 865 - "compositePrimaryKeys": {}, 866 - "uniqueConstraints": { 867 - "dropbox_paths_file_id_unique": { 868 - "name": "dropbox_paths_file_id_unique", 869 - "nullsNotDistinct": false, 870 - "columns": [ 871 - "file_id" 872 - ] 873 - } 874 - }, 875 - "policies": {}, 876 - "checkConstraints": {}, 877 - "isRLSEnabled": false 878 - }, 879 - "public.dropbox_tokens": { 880 - "name": "dropbox_tokens", 881 - "schema": "", 882 - "columns": { 883 - "xata_id": { 884 - "name": "xata_id", 885 - "type": "text", 886 - "primaryKey": true, 887 - "notNull": true 888 - }, 889 - "refresh_token": { 890 - "name": "refresh_token", 891 - "type": "text", 892 - "primaryKey": false, 893 - "notNull": true 894 - }, 895 - "xata_createdat": { 896 - "name": "xata_createdat", 897 - "type": "timestamp", 898 - "primaryKey": false, 899 - "notNull": true, 900 - "default": "now()" 901 - }, 902 - "xata_updatedat": { 903 - "name": "xata_updatedat", 904 - "type": "timestamp", 905 - "primaryKey": false, 906 - "notNull": true, 907 - "default": "now()" 908 - } 909 - }, 910 - "indexes": {}, 911 - "foreignKeys": {}, 912 - "compositePrimaryKeys": {}, 913 - "uniqueConstraints": {}, 914 - "policies": {}, 915 - "checkConstraints": {}, 916 - "isRLSEnabled": false 917 - }, 918 - "public.dropbox": { 919 - "name": "dropbox", 920 - "schema": "", 921 - "columns": { 922 - "xata_id": { 923 - "name": "xata_id", 924 - "type": "text", 925 - "primaryKey": true, 926 - "notNull": true 927 - }, 928 - "user_id": { 929 - "name": "user_id", 930 - "type": "text", 931 - "primaryKey": false, 932 - "notNull": true 933 - }, 934 - "dropbox_token_id": { 935 - "name": "dropbox_token_id", 936 - "type": "text", 937 - "primaryKey": false, 938 - "notNull": true 939 - }, 940 - "xata_version": { 941 - "name": "xata_version", 942 - "type": "text", 943 - "primaryKey": false, 944 - "notNull": true 945 - }, 946 - "xata_createdat": { 947 - "name": "xata_createdat", 948 - "type": "timestamp", 949 - "primaryKey": false, 950 - "notNull": true, 951 - "default": "now()" 952 - }, 953 - "xata_updatedat": { 954 - "name": "xata_updatedat", 955 - "type": "timestamp", 956 - "primaryKey": false, 957 - "notNull": true, 958 - "default": "now()" 959 - } 960 - }, 961 - "indexes": {}, 962 - "foreignKeys": { 963 - "dropbox_user_id_users_xata_id_fk": { 964 - "name": "dropbox_user_id_users_xata_id_fk", 965 - "tableFrom": "dropbox", 966 - "tableTo": "users", 967 - "columnsFrom": [ 968 - "user_id" 969 - ], 970 - "columnsTo": [ 971 - "xata_id" 972 - ], 973 - "onDelete": "no action", 974 - "onUpdate": "no action" 975 - }, 976 - "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk": { 977 - "name": "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk", 978 - "tableFrom": "dropbox", 979 - "tableTo": "dropbox_tokens", 980 - "columnsFrom": [ 981 - "dropbox_token_id" 982 - ], 983 - "columnsTo": [ 984 - "xata_id" 985 - ], 986 - "onDelete": "no action", 987 - "onUpdate": "no action" 988 - } 989 - }, 990 - "compositePrimaryKeys": {}, 991 - "uniqueConstraints": {}, 992 - "policies": {}, 993 - "checkConstraints": {}, 994 - "isRLSEnabled": false 995 - }, 996 - "public.google_drive_accounts": { 997 - "name": "google_drive_accounts", 998 - "schema": "", 999 - "columns": { 1000 - "xata_id": { 1001 - "name": "xata_id", 1002 - "type": "text", 1003 - "primaryKey": true, 1004 - "notNull": true 1005 - }, 1006 - "email": { 1007 - "name": "email", 1008 - "type": "text", 1009 - "primaryKey": false, 1010 - "notNull": true 1011 - }, 1012 - "is_beta_user": { 1013 - "name": "is_beta_user", 1014 - "type": "boolean", 1015 - "primaryKey": false, 1016 - "notNull": true, 1017 - "default": false 1018 - }, 1019 - "user_id": { 1020 - "name": "user_id", 1021 - "type": "text", 1022 - "primaryKey": false, 1023 - "notNull": true 1024 - }, 1025 - "xata_version": { 1026 - "name": "xata_version", 1027 - "type": "text", 1028 - "primaryKey": false, 1029 - "notNull": true 1030 - }, 1031 - "xata_createdat": { 1032 - "name": "xata_createdat", 1033 - "type": "timestamp", 1034 - "primaryKey": false, 1035 - "notNull": true, 1036 - "default": "now()" 1037 - }, 1038 - "xata_updatedat": { 1039 - "name": "xata_updatedat", 1040 - "type": "timestamp", 1041 - "primaryKey": false, 1042 - "notNull": true, 1043 - "default": "now()" 1044 - } 1045 - }, 1046 - "indexes": {}, 1047 - "foreignKeys": { 1048 - "google_drive_accounts_user_id_users_xata_id_fk": { 1049 - "name": "google_drive_accounts_user_id_users_xata_id_fk", 1050 - "tableFrom": "google_drive_accounts", 1051 - "tableTo": "users", 1052 - "columnsFrom": [ 1053 - "user_id" 1054 - ], 1055 - "columnsTo": [ 1056 - "xata_id" 1057 - ], 1058 - "onDelete": "no action", 1059 - "onUpdate": "no action" 1060 - } 1061 - }, 1062 - "compositePrimaryKeys": {}, 1063 - "uniqueConstraints": { 1064 - "google_drive_accounts_email_unique": { 1065 - "name": "google_drive_accounts_email_unique", 1066 - "nullsNotDistinct": false, 1067 - "columns": [ 1068 - "email" 1069 - ] 1070 - } 1071 - }, 1072 - "policies": {}, 1073 - "checkConstraints": {}, 1074 - "isRLSEnabled": false 1075 - }, 1076 - "public.google_drive_directories": { 1077 - "name": "google_drive_directories", 1078 - "schema": "", 1079 - "columns": { 1080 - "xata_id": { 1081 - "name": "xata_id", 1082 - "type": "text", 1083 - "primaryKey": true, 1084 - "notNull": true 1085 - }, 1086 - "name": { 1087 - "name": "name", 1088 - "type": "text", 1089 - "primaryKey": false, 1090 - "notNull": true 1091 - }, 1092 - "path": { 1093 - "name": "path", 1094 - "type": "text", 1095 - "primaryKey": false, 1096 - "notNull": true 1097 - }, 1098 - "parent_id": { 1099 - "name": "parent_id", 1100 - "type": "text", 1101 - "primaryKey": false, 1102 - "notNull": false 1103 - }, 1104 - "google_drive_id": { 1105 - "name": "google_drive_id", 1106 - "type": "text", 1107 - "primaryKey": false, 1108 - "notNull": true 1109 - }, 1110 - "file_id": { 1111 - "name": "file_id", 1112 - "type": "text", 1113 - "primaryKey": false, 1114 - "notNull": true 1115 - }, 1116 - "xata_version": { 1117 - "name": "xata_version", 1118 - "type": "text", 1119 - "primaryKey": false, 1120 - "notNull": true 1121 - }, 1122 - "xata_createdat": { 1123 - "name": "xata_createdat", 1124 - "type": "timestamp", 1125 - "primaryKey": false, 1126 - "notNull": true, 1127 - "default": "now()" 1128 - }, 1129 - "xata_updatedat": { 1130 - "name": "xata_updatedat", 1131 - "type": "timestamp", 1132 - "primaryKey": false, 1133 - "notNull": true, 1134 - "default": "now()" 1135 - } 1136 - }, 1137 - "indexes": {}, 1138 - "foreignKeys": { 1139 - "google_drive_directories_parent_id_google_drive_directories_xata_id_fk": { 1140 - "name": "google_drive_directories_parent_id_google_drive_directories_xata_id_fk", 1141 - "tableFrom": "google_drive_directories", 1142 - "tableTo": "google_drive_directories", 1143 - "columnsFrom": [ 1144 - "parent_id" 1145 - ], 1146 - "columnsTo": [ 1147 - "xata_id" 1148 - ], 1149 - "onDelete": "no action", 1150 - "onUpdate": "no action" 1151 - } 1152 - }, 1153 - "compositePrimaryKeys": {}, 1154 - "uniqueConstraints": { 1155 - "google_drive_directories_file_id_unique": { 1156 - "name": "google_drive_directories_file_id_unique", 1157 - "nullsNotDistinct": false, 1158 - "columns": [ 1159 - "file_id" 1160 - ] 1161 - } 1162 - }, 1163 - "policies": {}, 1164 - "checkConstraints": {}, 1165 - "isRLSEnabled": false 1166 - }, 1167 - "public.google_drive_paths": { 1168 - "name": "google_drive_paths", 1169 - "schema": "", 1170 - "columns": { 1171 - "xata_id": { 1172 - "name": "xata_id", 1173 - "type": "text", 1174 - "primaryKey": true, 1175 - "notNull": true 1176 - }, 1177 - "google_drive_id": { 1178 - "name": "google_drive_id", 1179 - "type": "text", 1180 - "primaryKey": false, 1181 - "notNull": true 1182 - }, 1183 - "track_id": { 1184 - "name": "track_id", 1185 - "type": "text", 1186 - "primaryKey": false, 1187 - "notNull": true 1188 - }, 1189 - "name": { 1190 - "name": "name", 1191 - "type": "text", 1192 - "primaryKey": false, 1193 - "notNull": true 1194 - }, 1195 - "directory_id": { 1196 - "name": "directory_id", 1197 - "type": "text", 1198 - "primaryKey": false, 1199 - "notNull": false 1200 - }, 1201 - "file_id": { 1202 - "name": "file_id", 1203 - "type": "text", 1204 - "primaryKey": false, 1205 - "notNull": true 1206 - }, 1207 - "xata_version": { 1208 - "name": "xata_version", 1209 - "type": "text", 1210 - "primaryKey": false, 1211 - "notNull": true 1212 - }, 1213 - "xata_createdat": { 1214 - "name": "xata_createdat", 1215 - "type": "timestamp", 1216 - "primaryKey": false, 1217 - "notNull": true, 1218 - "default": "now()" 1219 - }, 1220 - "xata_updatedat": { 1221 - "name": "xata_updatedat", 1222 - "type": "timestamp", 1223 - "primaryKey": false, 1224 - "notNull": true, 1225 - "default": "now()" 1226 - } 1227 - }, 1228 - "indexes": {}, 1229 - "foreignKeys": { 1230 - "google_drive_paths_directory_id_google_drive_directories_xata_id_fk": { 1231 - "name": "google_drive_paths_directory_id_google_drive_directories_xata_id_fk", 1232 - "tableFrom": "google_drive_paths", 1233 - "tableTo": "google_drive_directories", 1234 - "columnsFrom": [ 1235 - "directory_id" 1236 - ], 1237 - "columnsTo": [ 1238 - "xata_id" 1239 - ], 1240 - "onDelete": "no action", 1241 - "onUpdate": "no action" 1242 - } 1243 - }, 1244 - "compositePrimaryKeys": {}, 1245 - "uniqueConstraints": { 1246 - "google_drive_paths_file_id_unique": { 1247 - "name": "google_drive_paths_file_id_unique", 1248 - "nullsNotDistinct": false, 1249 - "columns": [ 1250 - "file_id" 1251 - ] 1252 - } 1253 - }, 1254 - "policies": {}, 1255 - "checkConstraints": {}, 1256 - "isRLSEnabled": false 1257 - }, 1258 - "public.google_drive_tokens": { 1259 - "name": "google_drive_tokens", 1260 - "schema": "", 1261 - "columns": { 1262 - "xata_id": { 1263 - "name": "xata_id", 1264 - "type": "text", 1265 - "primaryKey": true, 1266 - "notNull": true 1267 - }, 1268 - "refresh_token": { 1269 - "name": "refresh_token", 1270 - "type": "text", 1271 - "primaryKey": false, 1272 - "notNull": true 1273 - }, 1274 - "xata_createdat": { 1275 - "name": "xata_createdat", 1276 - "type": "timestamp", 1277 - "primaryKey": false, 1278 - "notNull": true, 1279 - "default": "now()" 1280 - }, 1281 - "xata_updatedat": { 1282 - "name": "xata_updatedat", 1283 - "type": "timestamp", 1284 - "primaryKey": false, 1285 - "notNull": true, 1286 - "default": "now()" 1287 - } 1288 - }, 1289 - "indexes": {}, 1290 - "foreignKeys": {}, 1291 - "compositePrimaryKeys": {}, 1292 - "uniqueConstraints": {}, 1293 - "policies": {}, 1294 - "checkConstraints": {}, 1295 - "isRLSEnabled": false 1296 - }, 1297 - "public.google_drive": { 1298 - "name": "google_drive", 1299 - "schema": "", 1300 - "columns": { 1301 - "xata_id": { 1302 - "name": "xata_id", 1303 - "type": "text", 1304 - "primaryKey": true, 1305 - "notNull": true 1306 - }, 1307 - "google_drive_token_id": { 1308 - "name": "google_drive_token_id", 1309 - "type": "text", 1310 - "primaryKey": false, 1311 - "notNull": true 1312 - }, 1313 - "user_id": { 1314 - "name": "user_id", 1315 - "type": "text", 1316 - "primaryKey": false, 1317 - "notNull": true 1318 - }, 1319 - "xata_version": { 1320 - "name": "xata_version", 1321 - "type": "text", 1322 - "primaryKey": false, 1323 - "notNull": true 1324 - }, 1325 - "xata_createdat": { 1326 - "name": "xata_createdat", 1327 - "type": "timestamp", 1328 - "primaryKey": false, 1329 - "notNull": true, 1330 - "default": "now()" 1331 - }, 1332 - "xata_updatedat": { 1333 - "name": "xata_updatedat", 1334 - "type": "timestamp", 1335 - "primaryKey": false, 1336 - "notNull": true, 1337 - "default": "now()" 1338 - } 1339 - }, 1340 - "indexes": {}, 1341 - "foreignKeys": { 1342 - "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk": { 1343 - "name": "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk", 1344 - "tableFrom": "google_drive", 1345 - "tableTo": "google_drive_tokens", 1346 - "columnsFrom": [ 1347 - "google_drive_token_id" 1348 - ], 1349 - "columnsTo": [ 1350 - "xata_id" 1351 - ], 1352 - "onDelete": "no action", 1353 - "onUpdate": "no action" 1354 - }, 1355 - "google_drive_user_id_users_xata_id_fk": { 1356 - "name": "google_drive_user_id_users_xata_id_fk", 1357 - "tableFrom": "google_drive", 1358 - "tableTo": "users", 1359 - "columnsFrom": [ 1360 - "user_id" 1361 - ], 1362 - "columnsTo": [ 1363 - "xata_id" 1364 - ], 1365 - "onDelete": "no action", 1366 - "onUpdate": "no action" 1367 - } 1368 - }, 1369 - "compositePrimaryKeys": {}, 1370 - "uniqueConstraints": {}, 1371 - "policies": {}, 1372 - "checkConstraints": {}, 1373 - "isRLSEnabled": false 1374 - }, 1375 - "public.loved_tracks": { 1376 - "name": "loved_tracks", 1377 - "schema": "", 1378 - "columns": { 1379 - "xata_id": { 1380 - "name": "xata_id", 1381 - "type": "text", 1382 - "primaryKey": true, 1383 - "notNull": true 1384 - }, 1385 - "user_id": { 1386 - "name": "user_id", 1387 - "type": "text", 1388 - "primaryKey": false, 1389 - "notNull": true 1390 - }, 1391 - "track_id": { 1392 - "name": "track_id", 1393 - "type": "text", 1394 - "primaryKey": false, 1395 - "notNull": true 1396 - }, 1397 - "uri": { 1398 - "name": "uri", 1399 - "type": "text", 1400 - "primaryKey": false, 1401 - "notNull": false 1402 - }, 1403 - "xata_createdat": { 1404 - "name": "xata_createdat", 1405 - "type": "timestamp", 1406 - "primaryKey": false, 1407 - "notNull": true, 1408 - "default": "now()" 1409 - } 1410 - }, 1411 - "indexes": {}, 1412 - "foreignKeys": { 1413 - "loved_tracks_user_id_users_xata_id_fk": { 1414 - "name": "loved_tracks_user_id_users_xata_id_fk", 1415 - "tableFrom": "loved_tracks", 1416 - "tableTo": "users", 1417 - "columnsFrom": [ 1418 - "user_id" 1419 - ], 1420 - "columnsTo": [ 1421 - "xata_id" 1422 - ], 1423 - "onDelete": "no action", 1424 - "onUpdate": "no action" 1425 - }, 1426 - "loved_tracks_track_id_tracks_xata_id_fk": { 1427 - "name": "loved_tracks_track_id_tracks_xata_id_fk", 1428 - "tableFrom": "loved_tracks", 1429 - "tableTo": "tracks", 1430 - "columnsFrom": [ 1431 - "track_id" 1432 - ], 1433 - "columnsTo": [ 1434 - "xata_id" 1435 - ], 1436 - "onDelete": "no action", 1437 - "onUpdate": "no action" 1438 - } 1439 - }, 1440 - "compositePrimaryKeys": {}, 1441 - "uniqueConstraints": { 1442 - "loved_tracks_uri_unique": { 1443 - "name": "loved_tracks_uri_unique", 1444 - "nullsNotDistinct": false, 1445 - "columns": [ 1446 - "uri" 1447 - ] 1448 - } 1449 - }, 1450 - "policies": {}, 1451 - "checkConstraints": {}, 1452 - "isRLSEnabled": false 1453 - }, 1454 - "public.playlist_tracks": { 1455 - "name": "playlist_tracks", 1456 - "schema": "", 1457 - "columns": { 1458 - "xata_id": { 1459 - "name": "xata_id", 1460 - "type": "text", 1461 - "primaryKey": true, 1462 - "notNull": true 1463 - }, 1464 - "playlist_id": { 1465 - "name": "playlist_id", 1466 - "type": "text", 1467 - "primaryKey": false, 1468 - "notNull": true 1469 - }, 1470 - "track_id": { 1471 - "name": "track_id", 1472 - "type": "text", 1473 - "primaryKey": false, 1474 - "notNull": true 1475 - }, 1476 - "xata_createdat": { 1477 - "name": "xata_createdat", 1478 - "type": "timestamp", 1479 - "primaryKey": false, 1480 - "notNull": true, 1481 - "default": "now()" 1482 - } 1483 - }, 1484 - "indexes": {}, 1485 - "foreignKeys": { 1486 - "playlist_tracks_playlist_id_playlists_xata_id_fk": { 1487 - "name": "playlist_tracks_playlist_id_playlists_xata_id_fk", 1488 - "tableFrom": "playlist_tracks", 1489 - "tableTo": "playlists", 1490 - "columnsFrom": [ 1491 - "playlist_id" 1492 - ], 1493 - "columnsTo": [ 1494 - "xata_id" 1495 - ], 1496 - "onDelete": "no action", 1497 - "onUpdate": "no action" 1498 - }, 1499 - "playlist_tracks_track_id_tracks_xata_id_fk": { 1500 - "name": "playlist_tracks_track_id_tracks_xata_id_fk", 1501 - "tableFrom": "playlist_tracks", 1502 - "tableTo": "tracks", 1503 - "columnsFrom": [ 1504 - "track_id" 1505 - ], 1506 - "columnsTo": [ 1507 - "xata_id" 1508 - ], 1509 - "onDelete": "no action", 1510 - "onUpdate": "no action" 1511 - } 1512 - }, 1513 - "compositePrimaryKeys": {}, 1514 - "uniqueConstraints": {}, 1515 - "policies": {}, 1516 - "checkConstraints": {}, 1517 - "isRLSEnabled": false 1518 - }, 1519 - "public.playlists": { 1520 - "name": "playlists", 1521 - "schema": "", 1522 - "columns": { 1523 - "xata_id": { 1524 - "name": "xata_id", 1525 - "type": "text", 1526 - "primaryKey": true, 1527 - "notNull": true 1528 - }, 1529 - "name": { 1530 - "name": "name", 1531 - "type": "text", 1532 - "primaryKey": false, 1533 - "notNull": true 1534 - }, 1535 - "picture": { 1536 - "name": "picture", 1537 - "type": "text", 1538 - "primaryKey": false, 1539 - "notNull": false 1540 - }, 1541 - "description": { 1542 - "name": "description", 1543 - "type": "text", 1544 - "primaryKey": false, 1545 - "notNull": false 1546 - }, 1547 - "uri": { 1548 - "name": "uri", 1549 - "type": "text", 1550 - "primaryKey": false, 1551 - "notNull": false 1552 - }, 1553 - "spotify_link": { 1554 - "name": "spotify_link", 1555 - "type": "text", 1556 - "primaryKey": false, 1557 - "notNull": false 1558 - }, 1559 - "tidal_link": { 1560 - "name": "tidal_link", 1561 - "type": "text", 1562 - "primaryKey": false, 1563 - "notNull": false 1564 - }, 1565 - "apple_music_link": { 1566 - "name": "apple_music_link", 1567 - "type": "text", 1568 - "primaryKey": false, 1569 - "notNull": false 1570 - }, 1571 - "created_by": { 1572 - "name": "created_by", 1573 - "type": "text", 1574 - "primaryKey": false, 1575 - "notNull": true 1576 - }, 1577 - "xata_createdat": { 1578 - "name": "xata_createdat", 1579 - "type": "timestamp", 1580 - "primaryKey": false, 1581 - "notNull": true, 1582 - "default": "now()" 1583 - }, 1584 - "xata_updatedat": { 1585 - "name": "xata_updatedat", 1586 - "type": "timestamp", 1587 - "primaryKey": false, 1588 - "notNull": true, 1589 - "default": "now()" 1590 - } 1591 - }, 1592 - "indexes": {}, 1593 - "foreignKeys": { 1594 - "playlists_created_by_users_xata_id_fk": { 1595 - "name": "playlists_created_by_users_xata_id_fk", 1596 - "tableFrom": "playlists", 1597 - "tableTo": "users", 1598 - "columnsFrom": [ 1599 - "created_by" 1600 - ], 1601 - "columnsTo": [ 1602 - "xata_id" 1603 - ], 1604 - "onDelete": "no action", 1605 - "onUpdate": "no action" 1606 - } 1607 - }, 1608 - "compositePrimaryKeys": {}, 1609 - "uniqueConstraints": { 1610 - "playlists_uri_unique": { 1611 - "name": "playlists_uri_unique", 1612 - "nullsNotDistinct": false, 1613 - "columns": [ 1614 - "uri" 1615 - ] 1616 - } 1617 - }, 1618 - "policies": {}, 1619 - "checkConstraints": {}, 1620 - "isRLSEnabled": false 1621 - }, 1622 - "public.profile_shouts": { 1623 - "name": "profile_shouts", 1624 - "schema": "", 1625 - "columns": { 1626 - "xata_id": { 1627 - "name": "xata_id", 1628 - "type": "text", 1629 - "primaryKey": true, 1630 - "notNull": true 1631 - }, 1632 - "user_id": { 1633 - "name": "user_id", 1634 - "type": "text", 1635 - "primaryKey": false, 1636 - "notNull": true 1637 - }, 1638 - "shout_id": { 1639 - "name": "shout_id", 1640 - "type": "text", 1641 - "primaryKey": false, 1642 - "notNull": true 1643 - }, 1644 - "xata_createdat": { 1645 - "name": "xata_createdat", 1646 - "type": "timestamp", 1647 - "primaryKey": false, 1648 - "notNull": true, 1649 - "default": "now()" 1650 - } 1651 - }, 1652 - "indexes": {}, 1653 - "foreignKeys": { 1654 - "profile_shouts_user_id_users_xata_id_fk": { 1655 - "name": "profile_shouts_user_id_users_xata_id_fk", 1656 - "tableFrom": "profile_shouts", 1657 - "tableTo": "users", 1658 - "columnsFrom": [ 1659 - "user_id" 1660 - ], 1661 - "columnsTo": [ 1662 - "xata_id" 1663 - ], 1664 - "onDelete": "no action", 1665 - "onUpdate": "no action" 1666 - }, 1667 - "profile_shouts_shout_id_shouts_xata_id_fk": { 1668 - "name": "profile_shouts_shout_id_shouts_xata_id_fk", 1669 - "tableFrom": "profile_shouts", 1670 - "tableTo": "shouts", 1671 - "columnsFrom": [ 1672 - "shout_id" 1673 - ], 1674 - "columnsTo": [ 1675 - "xata_id" 1676 - ], 1677 - "onDelete": "no action", 1678 - "onUpdate": "no action" 1679 - } 1680 - }, 1681 - "compositePrimaryKeys": {}, 1682 - "uniqueConstraints": {}, 1683 - "policies": {}, 1684 - "checkConstraints": {}, 1685 - "isRLSEnabled": false 1686 - }, 1687 - "public.queue_tracks": { 1688 - "name": "queue_tracks", 1689 - "schema": "", 1690 - "columns": { 1691 - "xata_id": { 1692 - "name": "xata_id", 1693 - "type": "text", 1694 - "primaryKey": true, 1695 - "notNull": true 1696 - }, 1697 - "user_id": { 1698 - "name": "user_id", 1699 - "type": "text", 1700 - "primaryKey": false, 1701 - "notNull": true 1702 - }, 1703 - "track_id": { 1704 - "name": "track_id", 1705 - "type": "text", 1706 - "primaryKey": false, 1707 - "notNull": true 1708 - }, 1709 - "position": { 1710 - "name": "position", 1711 - "type": "integer", 1712 - "primaryKey": false, 1713 - "notNull": true 1714 - }, 1715 - "file_uri": { 1716 - "name": "file_uri", 1717 - "type": "text", 1718 - "primaryKey": false, 1719 - "notNull": true 1720 - }, 1721 - "xata_version": { 1722 - "name": "xata_version", 1723 - "type": "integer", 1724 - "primaryKey": false, 1725 - "notNull": true, 1726 - "default": 0 1727 - }, 1728 - "xata_createdat": { 1729 - "name": "xata_createdat", 1730 - "type": "timestamp", 1731 - "primaryKey": false, 1732 - "notNull": true, 1733 - "default": "now()" 1734 - }, 1735 - "xata_updatedat": { 1736 - "name": "xata_updatedat", 1737 - "type": "timestamp", 1738 - "primaryKey": false, 1739 - "notNull": true, 1740 - "default": "now()" 1741 - } 1742 - }, 1743 - "indexes": {}, 1744 - "foreignKeys": { 1745 - "queue_tracks_user_id_users_xata_id_fk": { 1746 - "name": "queue_tracks_user_id_users_xata_id_fk", 1747 - "tableFrom": "queue_tracks", 1748 - "tableTo": "users", 1749 - "columnsFrom": [ 1750 - "user_id" 1751 - ], 1752 - "columnsTo": [ 1753 - "xata_id" 1754 - ], 1755 - "onDelete": "no action", 1756 - "onUpdate": "no action" 1757 - }, 1758 - "queue_tracks_track_id_tracks_xata_id_fk": { 1759 - "name": "queue_tracks_track_id_tracks_xata_id_fk", 1760 - "tableFrom": "queue_tracks", 1761 - "tableTo": "tracks", 1762 - "columnsFrom": [ 1763 - "track_id" 1764 - ], 1765 - "columnsTo": [ 1766 - "xata_id" 1767 - ], 1768 - "onDelete": "no action", 1769 - "onUpdate": "no action" 1770 - } 1771 - }, 1772 - "compositePrimaryKeys": {}, 1773 - "uniqueConstraints": {}, 1774 - "policies": {}, 1775 - "checkConstraints": {}, 1776 - "isRLSEnabled": false 1777 - }, 1778 - "public.scrobbles": { 1779 - "name": "scrobbles", 1780 - "schema": "", 1781 - "columns": { 1782 - "xata_id": { 1783 - "name": "xata_id", 1784 - "type": "text", 1785 - "primaryKey": true, 1786 - "notNull": true 1787 - }, 1788 - "user_id": { 1789 - "name": "user_id", 1790 - "type": "text", 1791 - "primaryKey": false, 1792 - "notNull": false 1793 - }, 1794 - "track_id": { 1795 - "name": "track_id", 1796 - "type": "text", 1797 - "primaryKey": false, 1798 - "notNull": false 1799 - }, 1800 - "album_id": { 1801 - "name": "album_id", 1802 - "type": "text", 1803 - "primaryKey": false, 1804 - "notNull": false 1805 - }, 1806 - "artist_id": { 1807 - "name": "artist_id", 1808 - "type": "text", 1809 - "primaryKey": false, 1810 - "notNull": false 1811 - }, 1812 - "uri": { 1813 - "name": "uri", 1814 - "type": "text", 1815 - "primaryKey": false, 1816 - "notNull": false 1817 - }, 1818 - "xata_createdat": { 1819 - "name": "xata_createdat", 1820 - "type": "timestamp", 1821 - "primaryKey": false, 1822 - "notNull": true, 1823 - "default": "now()" 1824 - }, 1825 - "xata_updatedat": { 1826 - "name": "xata_updatedat", 1827 - "type": "timestamp", 1828 - "primaryKey": false, 1829 - "notNull": true, 1830 - "default": "now()" 1831 - }, 1832 - "xata_version": { 1833 - "name": "xata_version", 1834 - "type": "integer", 1835 - "primaryKey": false, 1836 - "notNull": false 1837 - }, 1838 - "timestamp": { 1839 - "name": "timestamp", 1840 - "type": "timestamp", 1841 - "primaryKey": false, 1842 - "notNull": true, 1843 - "default": "now()" 1844 - } 1845 - }, 1846 - "indexes": {}, 1847 - "foreignKeys": { 1848 - "scrobbles_user_id_users_xata_id_fk": { 1849 - "name": "scrobbles_user_id_users_xata_id_fk", 1850 - "tableFrom": "scrobbles", 1851 - "tableTo": "users", 1852 - "columnsFrom": [ 1853 - "user_id" 1854 - ], 1855 - "columnsTo": [ 1856 - "xata_id" 1857 - ], 1858 - "onDelete": "no action", 1859 - "onUpdate": "no action" 1860 - }, 1861 - "scrobbles_track_id_tracks_xata_id_fk": { 1862 - "name": "scrobbles_track_id_tracks_xata_id_fk", 1863 - "tableFrom": "scrobbles", 1864 - "tableTo": "tracks", 1865 - "columnsFrom": [ 1866 - "track_id" 1867 - ], 1868 - "columnsTo": [ 1869 - "xata_id" 1870 - ], 1871 - "onDelete": "no action", 1872 - "onUpdate": "no action" 1873 - }, 1874 - "scrobbles_album_id_albums_xata_id_fk": { 1875 - "name": "scrobbles_album_id_albums_xata_id_fk", 1876 - "tableFrom": "scrobbles", 1877 - "tableTo": "albums", 1878 - "columnsFrom": [ 1879 - "album_id" 1880 - ], 1881 - "columnsTo": [ 1882 - "xata_id" 1883 - ], 1884 - "onDelete": "no action", 1885 - "onUpdate": "no action" 1886 - }, 1887 - "scrobbles_artist_id_artists_xata_id_fk": { 1888 - "name": "scrobbles_artist_id_artists_xata_id_fk", 1889 - "tableFrom": "scrobbles", 1890 - "tableTo": "artists", 1891 - "columnsFrom": [ 1892 - "artist_id" 1893 - ], 1894 - "columnsTo": [ 1895 - "xata_id" 1896 - ], 1897 - "onDelete": "no action", 1898 - "onUpdate": "no action" 1899 - } 1900 - }, 1901 - "compositePrimaryKeys": {}, 1902 - "uniqueConstraints": { 1903 - "scrobbles_uri_unique": { 1904 - "name": "scrobbles_uri_unique", 1905 - "nullsNotDistinct": false, 1906 - "columns": [ 1907 - "uri" 1908 - ] 1909 - } 1910 - }, 1911 - "policies": {}, 1912 - "checkConstraints": {}, 1913 - "isRLSEnabled": false 1914 - }, 1915 - "public.shout_likes": { 1916 - "name": "shout_likes", 1917 - "schema": "", 1918 - "columns": { 1919 - "xata_id": { 1920 - "name": "xata_id", 1921 - "type": "text", 1922 - "primaryKey": true, 1923 - "notNull": true 1924 - }, 1925 - "user_id": { 1926 - "name": "user_id", 1927 - "type": "text", 1928 - "primaryKey": false, 1929 - "notNull": true 1930 - }, 1931 - "shout_id": { 1932 - "name": "shout_id", 1933 - "type": "text", 1934 - "primaryKey": false, 1935 - "notNull": true 1936 - }, 1937 - "xata_createdat": { 1938 - "name": "xata_createdat", 1939 - "type": "timestamp", 1940 - "primaryKey": false, 1941 - "notNull": true, 1942 - "default": "now()" 1943 - }, 1944 - "uri": { 1945 - "name": "uri", 1946 - "type": "text", 1947 - "primaryKey": false, 1948 - "notNull": true 1949 - } 1950 - }, 1951 - "indexes": {}, 1952 - "foreignKeys": { 1953 - "shout_likes_user_id_users_xata_id_fk": { 1954 - "name": "shout_likes_user_id_users_xata_id_fk", 1955 - "tableFrom": "shout_likes", 1956 - "tableTo": "users", 1957 - "columnsFrom": [ 1958 - "user_id" 1959 - ], 1960 - "columnsTo": [ 1961 - "xata_id" 1962 - ], 1963 - "onDelete": "no action", 1964 - "onUpdate": "no action" 1965 - }, 1966 - "shout_likes_shout_id_shouts_xata_id_fk": { 1967 - "name": "shout_likes_shout_id_shouts_xata_id_fk", 1968 - "tableFrom": "shout_likes", 1969 - "tableTo": "shouts", 1970 - "columnsFrom": [ 1971 - "shout_id" 1972 - ], 1973 - "columnsTo": [ 1974 - "xata_id" 1975 - ], 1976 - "onDelete": "no action", 1977 - "onUpdate": "no action" 1978 - } 1979 - }, 1980 - "compositePrimaryKeys": {}, 1981 - "uniqueConstraints": { 1982 - "shout_likes_uri_unique": { 1983 - "name": "shout_likes_uri_unique", 1984 - "nullsNotDistinct": false, 1985 - "columns": [ 1986 - "uri" 1987 - ] 1988 - } 1989 - }, 1990 - "policies": {}, 1991 - "checkConstraints": {}, 1992 - "isRLSEnabled": false 1993 - }, 1994 - "public.shout_reports": { 1995 - "name": "shout_reports", 1996 - "schema": "", 1997 - "columns": { 1998 - "xata_id": { 1999 - "name": "xata_id", 2000 - "type": "text", 2001 - "primaryKey": true, 2002 - "notNull": true 2003 - }, 2004 - "user_id": { 2005 - "name": "user_id", 2006 - "type": "text", 2007 - "primaryKey": false, 2008 - "notNull": true 2009 - }, 2010 - "shout_id": { 2011 - "name": "shout_id", 2012 - "type": "text", 2013 - "primaryKey": false, 2014 - "notNull": true 2015 - }, 2016 - "xata_createdat": { 2017 - "name": "xata_createdat", 2018 - "type": "timestamp", 2019 - "primaryKey": false, 2020 - "notNull": true, 2021 - "default": "now()" 2022 - } 2023 - }, 2024 - "indexes": {}, 2025 - "foreignKeys": { 2026 - "shout_reports_user_id_users_xata_id_fk": { 2027 - "name": "shout_reports_user_id_users_xata_id_fk", 2028 - "tableFrom": "shout_reports", 2029 - "tableTo": "users", 2030 - "columnsFrom": [ 2031 - "user_id" 2032 - ], 2033 - "columnsTo": [ 2034 - "xata_id" 2035 - ], 2036 - "onDelete": "no action", 2037 - "onUpdate": "no action" 2038 - }, 2039 - "shout_reports_shout_id_shouts_xata_id_fk": { 2040 - "name": "shout_reports_shout_id_shouts_xata_id_fk", 2041 - "tableFrom": "shout_reports", 2042 - "tableTo": "shouts", 2043 - "columnsFrom": [ 2044 - "shout_id" 2045 - ], 2046 - "columnsTo": [ 2047 - "xata_id" 2048 - ], 2049 - "onDelete": "no action", 2050 - "onUpdate": "no action" 2051 - } 2052 - }, 2053 - "compositePrimaryKeys": {}, 2054 - "uniqueConstraints": {}, 2055 - "policies": {}, 2056 - "checkConstraints": {}, 2057 - "isRLSEnabled": false 2058 - }, 2059 - "public.shouts": { 2060 - "name": "shouts", 2061 - "schema": "", 2062 - "columns": { 2063 - "xata_id": { 2064 - "name": "xata_id", 2065 - "type": "text", 2066 - "primaryKey": true, 2067 - "notNull": true 2068 - }, 2069 - "content": { 2070 - "name": "content", 2071 - "type": "text", 2072 - "primaryKey": false, 2073 - "notNull": true 2074 - }, 2075 - "track_id": { 2076 - "name": "track_id", 2077 - "type": "text", 2078 - "primaryKey": false, 2079 - "notNull": false 2080 - }, 2081 - "artist_id": { 2082 - "name": "artist_id", 2083 - "type": "text", 2084 - "primaryKey": false, 2085 - "notNull": false 2086 - }, 2087 - "album_id": { 2088 - "name": "album_id", 2089 - "type": "text", 2090 - "primaryKey": false, 2091 - "notNull": false 2092 - }, 2093 - "scrobble_id": { 2094 - "name": "scrobble_id", 2095 - "type": "text", 2096 - "primaryKey": false, 2097 - "notNull": false 2098 - }, 2099 - "uri": { 2100 - "name": "uri", 2101 - "type": "text", 2102 - "primaryKey": false, 2103 - "notNull": true 2104 - }, 2105 - "author_id": { 2106 - "name": "author_id", 2107 - "type": "text", 2108 - "primaryKey": false, 2109 - "notNull": true 2110 - }, 2111 - "parent_id": { 2112 - "name": "parent_id", 2113 - "type": "text", 2114 - "primaryKey": false, 2115 - "notNull": false 2116 - }, 2117 - "xata_createdat": { 2118 - "name": "xata_createdat", 2119 - "type": "timestamp", 2120 - "primaryKey": false, 2121 - "notNull": true, 2122 - "default": "now()" 2123 - }, 2124 - "xata_updatedat": { 2125 - "name": "xata_updatedat", 2126 - "type": "timestamp", 2127 - "primaryKey": false, 2128 - "notNull": true, 2129 - "default": "now()" 2130 - } 2131 - }, 2132 - "indexes": {}, 2133 - "foreignKeys": { 2134 - "shouts_track_id_tracks_xata_id_fk": { 2135 - "name": "shouts_track_id_tracks_xata_id_fk", 2136 - "tableFrom": "shouts", 2137 - "tableTo": "tracks", 2138 - "columnsFrom": [ 2139 - "track_id" 2140 - ], 2141 - "columnsTo": [ 2142 - "xata_id" 2143 - ], 2144 - "onDelete": "no action", 2145 - "onUpdate": "no action" 2146 - }, 2147 - "shouts_artist_id_users_xata_id_fk": { 2148 - "name": "shouts_artist_id_users_xata_id_fk", 2149 - "tableFrom": "shouts", 2150 - "tableTo": "users", 2151 - "columnsFrom": [ 2152 - "artist_id" 2153 - ], 2154 - "columnsTo": [ 2155 - "xata_id" 2156 - ], 2157 - "onDelete": "no action", 2158 - "onUpdate": "no action" 2159 - }, 2160 - "shouts_album_id_albums_xata_id_fk": { 2161 - "name": "shouts_album_id_albums_xata_id_fk", 2162 - "tableFrom": "shouts", 2163 - "tableTo": "albums", 2164 - "columnsFrom": [ 2165 - "album_id" 2166 - ], 2167 - "columnsTo": [ 2168 - "xata_id" 2169 - ], 2170 - "onDelete": "no action", 2171 - "onUpdate": "no action" 2172 - }, 2173 - "shouts_scrobble_id_scrobbles_xata_id_fk": { 2174 - "name": "shouts_scrobble_id_scrobbles_xata_id_fk", 2175 - "tableFrom": "shouts", 2176 - "tableTo": "scrobbles", 2177 - "columnsFrom": [ 2178 - "scrobble_id" 2179 - ], 2180 - "columnsTo": [ 2181 - "xata_id" 2182 - ], 2183 - "onDelete": "no action", 2184 - "onUpdate": "no action" 2185 - }, 2186 - "shouts_author_id_users_xata_id_fk": { 2187 - "name": "shouts_author_id_users_xata_id_fk", 2188 - "tableFrom": "shouts", 2189 - "tableTo": "users", 2190 - "columnsFrom": [ 2191 - "author_id" 2192 - ], 2193 - "columnsTo": [ 2194 - "xata_id" 2195 - ], 2196 - "onDelete": "no action", 2197 - "onUpdate": "no action" 2198 - }, 2199 - "shouts_parent_id_shouts_xata_id_fk": { 2200 - "name": "shouts_parent_id_shouts_xata_id_fk", 2201 - "tableFrom": "shouts", 2202 - "tableTo": "shouts", 2203 - "columnsFrom": [ 2204 - "parent_id" 2205 - ], 2206 - "columnsTo": [ 2207 - "xata_id" 2208 - ], 2209 - "onDelete": "no action", 2210 - "onUpdate": "no action" 2211 - } 2212 - }, 2213 - "compositePrimaryKeys": {}, 2214 - "uniqueConstraints": { 2215 - "shouts_uri_unique": { 2216 - "name": "shouts_uri_unique", 2217 - "nullsNotDistinct": false, 2218 - "columns": [ 2219 - "uri" 2220 - ] 2221 - } 2222 - }, 2223 - "policies": {}, 2224 - "checkConstraints": {}, 2225 - "isRLSEnabled": false 2226 - }, 2227 - "public.spotify_accounts": { 2228 - "name": "spotify_accounts", 2229 - "schema": "", 2230 - "columns": { 2231 - "xata_id": { 2232 - "name": "xata_id", 2233 - "type": "text", 2234 - "primaryKey": true, 2235 - "notNull": true 2236 - }, 2237 - "xata_version": { 2238 - "name": "xata_version", 2239 - "type": "integer", 2240 - "primaryKey": false, 2241 - "notNull": true 2242 - }, 2243 - "email": { 2244 - "name": "email", 2245 - "type": "text", 2246 - "primaryKey": false, 2247 - "notNull": true 2248 - }, 2249 - "user_id": { 2250 - "name": "user_id", 2251 - "type": "text", 2252 - "primaryKey": false, 2253 - "notNull": true 2254 - }, 2255 - "is_beta_user": { 2256 - "name": "is_beta_user", 2257 - "type": "boolean", 2258 - "primaryKey": false, 2259 - "notNull": true, 2260 - "default": false 2261 - }, 2262 - "xata_createdat": { 2263 - "name": "xata_createdat", 2264 - "type": "timestamp", 2265 - "primaryKey": false, 2266 - "notNull": true, 2267 - "default": "now()" 2268 - }, 2269 - "xata_updatedat": { 2270 - "name": "xata_updatedat", 2271 - "type": "timestamp", 2272 - "primaryKey": false, 2273 - "notNull": true, 2274 - "default": "now()" 2275 - } 2276 - }, 2277 - "indexes": {}, 2278 - "foreignKeys": { 2279 - "spotify_accounts_user_id_users_xata_id_fk": { 2280 - "name": "spotify_accounts_user_id_users_xata_id_fk", 2281 - "tableFrom": "spotify_accounts", 2282 - "tableTo": "users", 2283 - "columnsFrom": [ 2284 - "user_id" 2285 - ], 2286 - "columnsTo": [ 2287 - "xata_id" 2288 - ], 2289 - "onDelete": "no action", 2290 - "onUpdate": "no action" 2291 - } 2292 - }, 2293 - "compositePrimaryKeys": {}, 2294 - "uniqueConstraints": {}, 2295 - "policies": {}, 2296 - "checkConstraints": {}, 2297 - "isRLSEnabled": false 2298 - }, 2299 - "public.spotify_tokens": { 2300 - "name": "spotify_tokens", 2301 - "schema": "", 2302 - "columns": { 2303 - "xata_id": { 2304 - "name": "xata_id", 2305 - "type": "text", 2306 - "primaryKey": true, 2307 - "notNull": true 2308 - }, 2309 - "xata_version": { 2310 - "name": "xata_version", 2311 - "type": "integer", 2312 - "primaryKey": false, 2313 - "notNull": true 2314 - }, 2315 - "access_token": { 2316 - "name": "access_token", 2317 - "type": "text", 2318 - "primaryKey": false, 2319 - "notNull": true 2320 - }, 2321 - "refresh_token": { 2322 - "name": "refresh_token", 2323 - "type": "text", 2324 - "primaryKey": false, 2325 - "notNull": true 2326 - }, 2327 - "user_id": { 2328 - "name": "user_id", 2329 - "type": "text", 2330 - "primaryKey": false, 2331 - "notNull": true 2332 - }, 2333 - "xata_createdat": { 2334 - "name": "xata_createdat", 2335 - "type": "timestamp", 2336 - "primaryKey": false, 2337 - "notNull": true, 2338 - "default": "now()" 2339 - }, 2340 - "xata_updatedat": { 2341 - "name": "xata_updatedat", 2342 - "type": "timestamp", 2343 - "primaryKey": false, 2344 - "notNull": true, 2345 - "default": "now()" 2346 - } 2347 - }, 2348 - "indexes": {}, 2349 - "foreignKeys": { 2350 - "spotify_tokens_user_id_users_xata_id_fk": { 2351 - "name": "spotify_tokens_user_id_users_xata_id_fk", 2352 - "tableFrom": "spotify_tokens", 2353 - "tableTo": "users", 2354 - "columnsFrom": [ 2355 - "user_id" 2356 - ], 2357 - "columnsTo": [ 2358 - "xata_id" 2359 - ], 2360 - "onDelete": "no action", 2361 - "onUpdate": "no action" 2362 - } 2363 - }, 2364 - "compositePrimaryKeys": {}, 2365 - "uniqueConstraints": {}, 2366 - "policies": {}, 2367 - "checkConstraints": {}, 2368 - "isRLSEnabled": false 2369 - }, 2370 - "public.tracks": { 2371 - "name": "tracks", 2372 - "schema": "", 2373 - "columns": { 2374 - "xata_id": { 2375 - "name": "xata_id", 2376 - "type": "text", 2377 - "primaryKey": true, 2378 - "notNull": true 2379 - }, 2380 - "title": { 2381 - "name": "title", 2382 - "type": "text", 2383 - "primaryKey": false, 2384 - "notNull": true 2385 - }, 2386 - "artist": { 2387 - "name": "artist", 2388 - "type": "text", 2389 - "primaryKey": false, 2390 - "notNull": true 2391 - }, 2392 - "album_artist": { 2393 - "name": "album_artist", 2394 - "type": "text", 2395 - "primaryKey": false, 2396 - "notNull": true 2397 - }, 2398 - "album_art": { 2399 - "name": "album_art", 2400 - "type": "text", 2401 - "primaryKey": false, 2402 - "notNull": false 2403 - }, 2404 - "album": { 2405 - "name": "album", 2406 - "type": "text", 2407 - "primaryKey": false, 2408 - "notNull": true 2409 - }, 2410 - "track_number": { 2411 - "name": "track_number", 2412 - "type": "integer", 2413 - "primaryKey": false, 2414 - "notNull": false 2415 - }, 2416 - "duration": { 2417 - "name": "duration", 2418 - "type": "integer", 2419 - "primaryKey": false, 2420 - "notNull": true 2421 - }, 2422 - "mb_id": { 2423 - "name": "mb_id", 2424 - "type": "text", 2425 - "primaryKey": false, 2426 - "notNull": false 2427 - }, 2428 - "youtube_link": { 2429 - "name": "youtube_link", 2430 - "type": "text", 2431 - "primaryKey": false, 2432 - "notNull": false 2433 - }, 2434 - "spotify_link": { 2435 - "name": "spotify_link", 2436 - "type": "text", 2437 - "primaryKey": false, 2438 - "notNull": false 2439 - }, 2440 - "apple_music_link": { 2441 - "name": "apple_music_link", 2442 - "type": "text", 2443 - "primaryKey": false, 2444 - "notNull": false 2445 - }, 2446 - "tidal_link": { 2447 - "name": "tidal_link", 2448 - "type": "text", 2449 - "primaryKey": false, 2450 - "notNull": false 2451 - }, 2452 - "sha256": { 2453 - "name": "sha256", 2454 - "type": "text", 2455 - "primaryKey": false, 2456 - "notNull": true 2457 - }, 2458 - "disc_number": { 2459 - "name": "disc_number", 2460 - "type": "integer", 2461 - "primaryKey": false, 2462 - "notNull": false 2463 - }, 2464 - "lyrics": { 2465 - "name": "lyrics", 2466 - "type": "text", 2467 - "primaryKey": false, 2468 - "notNull": false 2469 - }, 2470 - "composer": { 2471 - "name": "composer", 2472 - "type": "text", 2473 - "primaryKey": false, 2474 - "notNull": false 2475 - }, 2476 - "genre": { 2477 - "name": "genre", 2478 - "type": "text", 2479 - "primaryKey": false, 2480 - "notNull": false 2481 - }, 2482 - "label": { 2483 - "name": "label", 2484 - "type": "text", 2485 - "primaryKey": false, 2486 - "notNull": false 2487 - }, 2488 - "copyright_message": { 2489 - "name": "copyright_message", 2490 - "type": "text", 2491 - "primaryKey": false, 2492 - "notNull": false 2493 - }, 2494 - "uri": { 2495 - "name": "uri", 2496 - "type": "text", 2497 - "primaryKey": false, 2498 - "notNull": false 2499 - }, 2500 - "album_uri": { 2501 - "name": "album_uri", 2502 - "type": "text", 2503 - "primaryKey": false, 2504 - "notNull": false 2505 - }, 2506 - "artist_uri": { 2507 - "name": "artist_uri", 2508 - "type": "text", 2509 - "primaryKey": false, 2510 - "notNull": false 2511 - }, 2512 - "xata_createdat": { 2513 - "name": "xata_createdat", 2514 - "type": "timestamp", 2515 - "primaryKey": false, 2516 - "notNull": true, 2517 - "default": "now()" 2518 - }, 2519 - "xata_updatedat": { 2520 - "name": "xata_updatedat", 2521 - "type": "timestamp", 2522 - "primaryKey": false, 2523 - "notNull": true, 2524 - "default": "now()" 2525 - }, 2526 - "xata_version": { 2527 - "name": "xata_version", 2528 - "type": "integer", 2529 - "primaryKey": false, 2530 - "notNull": false 2531 - } 2532 - }, 2533 - "indexes": {}, 2534 - "foreignKeys": {}, 2535 - "compositePrimaryKeys": {}, 2536 - "uniqueConstraints": { 2537 - "tracks_mb_id_unique": { 2538 - "name": "tracks_mb_id_unique", 2539 - "nullsNotDistinct": false, 2540 - "columns": [ 2541 - "mb_id" 2542 - ] 2543 - }, 2544 - "tracks_youtube_link_unique": { 2545 - "name": "tracks_youtube_link_unique", 2546 - "nullsNotDistinct": false, 2547 - "columns": [ 2548 - "youtube_link" 2549 - ] 2550 - }, 2551 - "tracks_spotify_link_unique": { 2552 - "name": "tracks_spotify_link_unique", 2553 - "nullsNotDistinct": false, 2554 - "columns": [ 2555 - "spotify_link" 2556 - ] 2557 - }, 2558 - "tracks_apple_music_link_unique": { 2559 - "name": "tracks_apple_music_link_unique", 2560 - "nullsNotDistinct": false, 2561 - "columns": [ 2562 - "apple_music_link" 2563 - ] 2564 - }, 2565 - "tracks_tidal_link_unique": { 2566 - "name": "tracks_tidal_link_unique", 2567 - "nullsNotDistinct": false, 2568 - "columns": [ 2569 - "tidal_link" 2570 - ] 2571 - }, 2572 - "tracks_sha256_unique": { 2573 - "name": "tracks_sha256_unique", 2574 - "nullsNotDistinct": false, 2575 - "columns": [ 2576 - "sha256" 2577 - ] 2578 - }, 2579 - "tracks_uri_unique": { 2580 - "name": "tracks_uri_unique", 2581 - "nullsNotDistinct": false, 2582 - "columns": [ 2583 - "uri" 2584 - ] 2585 - }, 2586 - "tracks_album_uri_unique": { 2587 - "name": "tracks_album_uri_unique", 2588 - "nullsNotDistinct": false, 2589 - "columns": [ 2590 - "album_uri" 2591 - ] 2592 - }, 2593 - "tracks_artist_uri_unique": { 2594 - "name": "tracks_artist_uri_unique", 2595 - "nullsNotDistinct": false, 2596 - "columns": [ 2597 - "artist_uri" 2598 - ] 2599 - } 2600 - }, 2601 - "policies": {}, 2602 - "checkConstraints": {}, 2603 - "isRLSEnabled": false 2604 - }, 2605 - "public.user_albums": { 2606 - "name": "user_albums", 2607 - "schema": "", 2608 - "columns": { 2609 - "xata_id": { 2610 - "name": "xata_id", 2611 - "type": "text", 2612 - "primaryKey": true, 2613 - "notNull": true 2614 - }, 2615 - "user_id": { 2616 - "name": "user_id", 2617 - "type": "text", 2618 - "primaryKey": false, 2619 - "notNull": true 2620 - }, 2621 - "album_id": { 2622 - "name": "album_id", 2623 - "type": "text", 2624 - "primaryKey": false, 2625 - "notNull": true 2626 - }, 2627 - "xata_createdat": { 2628 - "name": "xata_createdat", 2629 - "type": "timestamp", 2630 - "primaryKey": false, 2631 - "notNull": true, 2632 - "default": "now()" 2633 - }, 2634 - "xata_updatedat": { 2635 - "name": "xata_updatedat", 2636 - "type": "timestamp", 2637 - "primaryKey": false, 2638 - "notNull": true, 2639 - "default": "now()" 2640 - }, 2641 - "xata_version": { 2642 - "name": "xata_version", 2643 - "type": "integer", 2644 - "primaryKey": false, 2645 - "notNull": true 2646 - }, 2647 - "scrobbles": { 2648 - "name": "scrobbles", 2649 - "type": "integer", 2650 - "primaryKey": false, 2651 - "notNull": false 2652 - }, 2653 - "uri": { 2654 - "name": "uri", 2655 - "type": "text", 2656 - "primaryKey": false, 2657 - "notNull": true 2658 - } 2659 - }, 2660 - "indexes": {}, 2661 - "foreignKeys": { 2662 - "user_albums_user_id_users_xata_id_fk": { 2663 - "name": "user_albums_user_id_users_xata_id_fk", 2664 - "tableFrom": "user_albums", 2665 - "tableTo": "users", 2666 - "columnsFrom": [ 2667 - "user_id" 2668 - ], 2669 - "columnsTo": [ 2670 - "xata_id" 2671 - ], 2672 - "onDelete": "no action", 2673 - "onUpdate": "no action" 2674 - }, 2675 - "user_albums_album_id_albums_xata_id_fk": { 2676 - "name": "user_albums_album_id_albums_xata_id_fk", 2677 - "tableFrom": "user_albums", 2678 - "tableTo": "albums", 2679 - "columnsFrom": [ 2680 - "album_id" 2681 - ], 2682 - "columnsTo": [ 2683 - "xata_id" 2684 - ], 2685 - "onDelete": "no action", 2686 - "onUpdate": "no action" 2687 - } 2688 - }, 2689 - "compositePrimaryKeys": {}, 2690 - "uniqueConstraints": { 2691 - "user_albums_uri_unique": { 2692 - "name": "user_albums_uri_unique", 2693 - "nullsNotDistinct": false, 2694 - "columns": [ 2695 - "uri" 2696 - ] 2697 - } 2698 - }, 2699 - "policies": {}, 2700 - "checkConstraints": {}, 2701 - "isRLSEnabled": false 2702 - }, 2703 - "public.user_artists": { 2704 - "name": "user_artists", 2705 - "schema": "", 2706 - "columns": { 2707 - "xata_id": { 2708 - "name": "xata_id", 2709 - "type": "text", 2710 - "primaryKey": true, 2711 - "notNull": true 2712 - }, 2713 - "user_id": { 2714 - "name": "user_id", 2715 - "type": "text", 2716 - "primaryKey": false, 2717 - "notNull": true 2718 - }, 2719 - "artist_id": { 2720 - "name": "artist_id", 2721 - "type": "text", 2722 - "primaryKey": false, 2723 - "notNull": true 2724 - }, 2725 - "xata_createdat": { 2726 - "name": "xata_createdat", 2727 - "type": "timestamp", 2728 - "primaryKey": false, 2729 - "notNull": true, 2730 - "default": "now()" 2731 - }, 2732 - "xata_updatedat": { 2733 - "name": "xata_updatedat", 2734 - "type": "timestamp", 2735 - "primaryKey": false, 2736 - "notNull": true, 2737 - "default": "now()" 2738 - }, 2739 - "xata_version": { 2740 - "name": "xata_version", 2741 - "type": "integer", 2742 - "primaryKey": false, 2743 - "notNull": true 2744 - }, 2745 - "scrobbles": { 2746 - "name": "scrobbles", 2747 - "type": "integer", 2748 - "primaryKey": false, 2749 - "notNull": false 2750 - }, 2751 - "uri": { 2752 - "name": "uri", 2753 - "type": "text", 2754 - "primaryKey": false, 2755 - "notNull": true 2756 - } 2757 - }, 2758 - "indexes": {}, 2759 - "foreignKeys": { 2760 - "user_artists_user_id_users_xata_id_fk": { 2761 - "name": "user_artists_user_id_users_xata_id_fk", 2762 - "tableFrom": "user_artists", 2763 - "tableTo": "users", 2764 - "columnsFrom": [ 2765 - "user_id" 2766 - ], 2767 - "columnsTo": [ 2768 - "xata_id" 2769 - ], 2770 - "onDelete": "no action", 2771 - "onUpdate": "no action" 2772 - }, 2773 - "user_artists_artist_id_artists_xata_id_fk": { 2774 - "name": "user_artists_artist_id_artists_xata_id_fk", 2775 - "tableFrom": "user_artists", 2776 - "tableTo": "artists", 2777 - "columnsFrom": [ 2778 - "artist_id" 2779 - ], 2780 - "columnsTo": [ 2781 - "xata_id" 2782 - ], 2783 - "onDelete": "no action", 2784 - "onUpdate": "no action" 2785 - } 2786 - }, 2787 - "compositePrimaryKeys": {}, 2788 - "uniqueConstraints": { 2789 - "user_artists_uri_unique": { 2790 - "name": "user_artists_uri_unique", 2791 - "nullsNotDistinct": false, 2792 - "columns": [ 2793 - "uri" 2794 - ] 2795 - } 2796 - }, 2797 - "policies": {}, 2798 - "checkConstraints": {}, 2799 - "isRLSEnabled": false 2800 - }, 2801 - "public.user_playlists": { 2802 - "name": "user_playlists", 2803 - "schema": "", 2804 - "columns": { 2805 - "xata_id": { 2806 - "name": "xata_id", 2807 - "type": "text", 2808 - "primaryKey": true, 2809 - "notNull": true 2810 - }, 2811 - "user_id": { 2812 - "name": "user_id", 2813 - "type": "text", 2814 - "primaryKey": false, 2815 - "notNull": true 2816 - }, 2817 - "playlist_id": { 2818 - "name": "playlist_id", 2819 - "type": "text", 2820 - "primaryKey": false, 2821 - "notNull": true 2822 - }, 2823 - "xata_createdat": { 2824 - "name": "xata_createdat", 2825 - "type": "timestamp", 2826 - "primaryKey": false, 2827 - "notNull": true, 2828 - "default": "now()" 2829 - }, 2830 - "uri": { 2831 - "name": "uri", 2832 - "type": "text", 2833 - "primaryKey": false, 2834 - "notNull": true 2835 - } 2836 - }, 2837 - "indexes": {}, 2838 - "foreignKeys": { 2839 - "user_playlists_user_id_users_xata_id_fk": { 2840 - "name": "user_playlists_user_id_users_xata_id_fk", 2841 - "tableFrom": "user_playlists", 2842 - "tableTo": "users", 2843 - "columnsFrom": [ 2844 - "user_id" 2845 - ], 2846 - "columnsTo": [ 2847 - "xata_id" 2848 - ], 2849 - "onDelete": "no action", 2850 - "onUpdate": "no action" 2851 - }, 2852 - "user_playlists_playlist_id_tracks_xata_id_fk": { 2853 - "name": "user_playlists_playlist_id_tracks_xata_id_fk", 2854 - "tableFrom": "user_playlists", 2855 - "tableTo": "tracks", 2856 - "columnsFrom": [ 2857 - "playlist_id" 2858 - ], 2859 - "columnsTo": [ 2860 - "xata_id" 2861 - ], 2862 - "onDelete": "no action", 2863 - "onUpdate": "no action" 2864 - } 2865 - }, 2866 - "compositePrimaryKeys": {}, 2867 - "uniqueConstraints": { 2868 - "user_playlists_uri_unique": { 2869 - "name": "user_playlists_uri_unique", 2870 - "nullsNotDistinct": false, 2871 - "columns": [ 2872 - "uri" 2873 - ] 2874 - } 2875 - }, 2876 - "policies": {}, 2877 - "checkConstraints": {}, 2878 - "isRLSEnabled": false 2879 - }, 2880 - "public.user_tracks": { 2881 - "name": "user_tracks", 2882 - "schema": "", 2883 - "columns": { 2884 - "xata_id": { 2885 - "name": "xata_id", 2886 - "type": "text", 2887 - "primaryKey": true, 2888 - "notNull": true 2889 - }, 2890 - "user_id": { 2891 - "name": "user_id", 2892 - "type": "text", 2893 - "primaryKey": false, 2894 - "notNull": true 2895 - }, 2896 - "track_id": { 2897 - "name": "track_id", 2898 - "type": "text", 2899 - "primaryKey": false, 2900 - "notNull": true 2901 - }, 2902 - "xata_createdat": { 2903 - "name": "xata_createdat", 2904 - "type": "timestamp", 2905 - "primaryKey": false, 2906 - "notNull": true, 2907 - "default": "now()" 2908 - }, 2909 - "xata_updatedat": { 2910 - "name": "xata_updatedat", 2911 - "type": "timestamp", 2912 - "primaryKey": false, 2913 - "notNull": true, 2914 - "default": "now()" 2915 - }, 2916 - "xata_version": { 2917 - "name": "xata_version", 2918 - "type": "integer", 2919 - "primaryKey": false, 2920 - "notNull": true 2921 - }, 2922 - "uri": { 2923 - "name": "uri", 2924 - "type": "text", 2925 - "primaryKey": false, 2926 - "notNull": true 2927 - }, 2928 - "scrobbles": { 2929 - "name": "scrobbles", 2930 - "type": "integer", 2931 - "primaryKey": false, 2932 - "notNull": false 2933 - } 2934 - }, 2935 - "indexes": {}, 2936 - "foreignKeys": { 2937 - "user_tracks_user_id_users_xata_id_fk": { 2938 - "name": "user_tracks_user_id_users_xata_id_fk", 2939 - "tableFrom": "user_tracks", 2940 - "tableTo": "users", 2941 - "columnsFrom": [ 2942 - "user_id" 2943 - ], 2944 - "columnsTo": [ 2945 - "xata_id" 2946 - ], 2947 - "onDelete": "no action", 2948 - "onUpdate": "no action" 2949 - }, 2950 - "user_tracks_track_id_tracks_xata_id_fk": { 2951 - "name": "user_tracks_track_id_tracks_xata_id_fk", 2952 - "tableFrom": "user_tracks", 2953 - "tableTo": "tracks", 2954 - "columnsFrom": [ 2955 - "track_id" 2956 - ], 2957 - "columnsTo": [ 2958 - "xata_id" 2959 - ], 2960 - "onDelete": "no action", 2961 - "onUpdate": "no action" 2962 - } 2963 - }, 2964 - "compositePrimaryKeys": {}, 2965 - "uniqueConstraints": { 2966 - "user_tracks_uri_unique": { 2967 - "name": "user_tracks_uri_unique", 2968 - "nullsNotDistinct": false, 2969 - "columns": [ 2970 - "uri" 2971 - ] 2972 - } 2973 - }, 2974 - "policies": {}, 2975 - "checkConstraints": {}, 2976 - "isRLSEnabled": false 2977 - }, 2978 - "public.users": { 2979 - "name": "users", 2980 - "schema": "", 2981 - "columns": { 2982 - "xata_id": { 2983 - "name": "xata_id", 2984 - "type": "text", 2985 - "primaryKey": true, 2986 - "notNull": true 2987 - }, 2988 - "did": { 2989 - "name": "did", 2990 - "type": "text", 2991 - "primaryKey": false, 2992 - "notNull": true 2993 - }, 2994 - "display_name": { 2995 - "name": "display_name", 2996 - "type": "text", 2997 - "primaryKey": false, 2998 - "notNull": true 2999 - }, 3000 - "handle": { 3001 - "name": "handle", 3002 - "type": "text", 3003 - "primaryKey": false, 3004 - "notNull": true 3005 - }, 3006 - "avatar": { 3007 - "name": "avatar", 3008 - "type": "text", 3009 - "primaryKey": false, 3010 - "notNull": true 3011 - }, 3012 - "xata_createdat": { 3013 - "name": "xata_createdat", 3014 - "type": "timestamp", 3015 - "primaryKey": false, 3016 - "notNull": true, 3017 - "default": "now()" 3018 - }, 3019 - "xata_updatedat": { 3020 - "name": "xata_updatedat", 3021 - "type": "timestamp", 3022 - "primaryKey": false, 3023 - "notNull": true, 3024 - "default": "now()" 3025 - }, 3026 - "xata_version": { 3027 - "name": "xata_version", 3028 - "type": "integer", 3029 - "primaryKey": false, 3030 - "notNull": false 3031 - } 3032 - }, 3033 - "indexes": {}, 3034 - "foreignKeys": {}, 3035 - "compositePrimaryKeys": {}, 3036 - "uniqueConstraints": { 3037 - "users_did_unique": { 3038 - "name": "users_did_unique", 3039 - "nullsNotDistinct": false, 3040 - "columns": [ 3041 - "did" 3042 - ] 3043 - }, 3044 - "users_handle_unique": { 3045 - "name": "users_handle_unique", 3046 - "nullsNotDistinct": false, 3047 - "columns": [ 3048 - "handle" 3049 - ] 3050 - } 3051 - }, 3052 - "policies": {}, 3053 - "checkConstraints": {}, 3054 - "isRLSEnabled": false 3055 - }, 3056 - "public.webscrobblers": { 3057 - "name": "webscrobblers", 3058 - "schema": "", 3059 - "columns": { 3060 - "xata_id": { 3061 - "name": "xata_id", 3062 - "type": "text", 3063 - "primaryKey": true, 3064 - "notNull": true 3065 - }, 3066 - "name": { 3067 - "name": "name", 3068 - "type": "text", 3069 - "primaryKey": false, 3070 - "notNull": true 3071 - }, 3072 - "uuid": { 3073 - "name": "uuid", 3074 - "type": "text", 3075 - "primaryKey": false, 3076 - "notNull": true 3077 - }, 3078 - "description": { 3079 - "name": "description", 3080 - "type": "text", 3081 - "primaryKey": false, 3082 - "notNull": false 3083 - }, 3084 - "enabled": { 3085 - "name": "enabled", 3086 - "type": "boolean", 3087 - "primaryKey": false, 3088 - "notNull": true, 3089 - "default": true 3090 - }, 3091 - "user_id": { 3092 - "name": "user_id", 3093 - "type": "text", 3094 - "primaryKey": false, 3095 - "notNull": true 3096 - }, 3097 - "xata_createdat": { 3098 - "name": "xata_createdat", 3099 - "type": "timestamp", 3100 - "primaryKey": false, 3101 - "notNull": true, 3102 - "default": "now()" 3103 - }, 3104 - "xata_updatedat": { 3105 - "name": "xata_updatedat", 3106 - "type": "timestamp", 3107 - "primaryKey": false, 3108 - "notNull": true, 3109 - "default": "now()" 3110 - } 3111 - }, 3112 - "indexes": {}, 3113 - "foreignKeys": { 3114 - "webscrobblers_user_id_users_xata_id_fk": { 3115 - "name": "webscrobblers_user_id_users_xata_id_fk", 3116 - "tableFrom": "webscrobblers", 3117 - "tableTo": "users", 3118 - "columnsFrom": [ 3119 - "user_id" 3120 - ], 3121 - "columnsTo": [ 3122 - "xata_id" 3123 - ], 3124 - "onDelete": "no action", 3125 - "onUpdate": "no action" 3126 - } 3127 - }, 3128 - "compositePrimaryKeys": {}, 3129 - "uniqueConstraints": {}, 3130 - "policies": {}, 3131 - "checkConstraints": {}, 3132 - "isRLSEnabled": false 3133 - } 3134 - }, 3135 - "enums": {}, 3136 - "schemas": {}, 3137 - "sequences": {}, 3138 - "roles": {}, 3139 - "policies": {}, 3140 - "views": {}, 3141 - "_meta": { 3142 - "columns": {}, 3143 - "schemas": {}, 3144 - "tables": {} 3145 - } 3146 - }
+2 -9
apps/api/drizzle/meta/_journal.json
··· 5 5 { 6 6 "idx": 0, 7 7 "version": "7", 8 - "when": 1753405608015, 9 - "tag": "0000_quiet_mister_sinister", 10 - "breakpoints": true 11 - }, 12 - { 13 - "idx": 1, 14 - "version": "7", 15 - "when": 1759657577245, 16 - "tag": "0001_woozy_saracen", 8 + "when": 1759736561936, 9 + "tag": "0000_greedy_vanisher", 17 10 "breakpoints": true 18 11 } 19 12 ]
+2 -2
apps/api/src/schema/tracks.ts
··· 23 23 label: text("label"), 24 24 copyrightMessage: text("copyright_message"), 25 25 uri: text("uri").unique(), 26 - albumUri: text("album_uri").unique(), 27 - artistUri: text("artist_uri").unique(), 26 + albumUri: text("album_uri"), 27 + artistUri: text("artist_uri"), 28 28 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 29 29 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 30 30 xataVersion: integer("xata_version"),
+1 -1
apps/api/src/schema/users.ts
··· 4 4 const users = pgTable("users", { 5 5 id: text("xata_id").primaryKey(), 6 6 did: text("did").unique().notNull(), 7 - displayName: text("display_name").notNull(), 7 + displayName: text("display_name"), 8 8 handle: text("handle").unique().notNull(), 9 9 avatar: text("avatar").notNull(), 10 10 createdAt: timestamp("xata_createdat").defaultNow().notNull(),
+280 -3
crates/pgpull/src/lib.rs
··· 3 3 mod repo; 4 4 mod xata; 5 5 6 - use anyhow::Error; 6 + use anyhow::{Context, Error}; 7 + use owo_colors::OwoColorize; 8 + use sqlx::postgres::PgPoolOptions; 7 9 8 10 pub async fn pull_data() -> Result<(), Error> { 9 11 if env::var("SOURCE_POSTGRES_URL").is_err() { ··· 13 15 std::process::exit(1); 14 16 } 15 17 16 - // Placeholder for the actual data pulling logic from PostgreSQL 17 - println!("Pulling data from PostgreSQL..."); 18 + let pool = PgPoolOptions::new() 19 + .max_connections(5) 20 + .connect(&env::var("SOURCE_POSTGRES_URL")?) 21 + .await?; 22 + 23 + let dest_pool = PgPoolOptions::new() 24 + .max_connections(5) 25 + .connect(&env::var("XATA_POSTGRES_URL")?) 26 + .await?; 27 + 28 + let pool_clone = pool.clone(); 29 + let dest_pool_clone = dest_pool.clone(); 30 + let album_sync = tokio::spawn(async move { 31 + let total_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM albums") 32 + .fetch_one(&pool_clone) 33 + .await?; 34 + let total_albums = total_albums.0; 35 + tracing::info!(total = %total_albums.magenta(), "Total albums to sync"); 36 + 37 + const BATCH_SIZE: usize = 1000; 38 + 39 + let start = 0; 40 + let mut i = 1; 41 + 42 + for offset in (start..total_albums).step_by(BATCH_SIZE) { 43 + let albums = 44 + repo::album::get_albums(&pool_clone, offset as i64, BATCH_SIZE as i64).await?; 45 + tracing::info!( 46 + offset = %offset.magenta(), 47 + end = %((offset + albums.len() as i64).min(total_albums)).magenta(), 48 + total = %total_albums.magenta(), 49 + "Fetched albums" 50 + ); 51 + for album in &albums { 52 + tracing::info!(title = %album.title.cyan(), i = %i.magenta(), total = %total_albums.magenta(), "Inserting album"); 53 + repo::album::insert_album(&dest_pool_clone, album).await?; 54 + i += 1; 55 + } 56 + } 57 + Ok::<(), Error>(()) 58 + }); 59 + 60 + let pool_clone = pool.clone(); 61 + let dest_pool_clone = dest_pool.clone(); 62 + let artist_sync = tokio::spawn(async move { 63 + let total_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artists") 64 + .fetch_one(&pool_clone) 65 + .await?; 66 + let total_artists = total_artists.0; 67 + tracing::info!(total = %total_artists.magenta(), "Total artists to sync"); 68 + 69 + const BATCH_SIZE: usize = 1000; 70 + 71 + let start = 0; 72 + let mut i = 1; 73 + 74 + for offset in (start..total_artists).step_by(BATCH_SIZE) { 75 + let artists = 76 + repo::artist::get_artists(&pool_clone, offset as i64, BATCH_SIZE as i64).await?; 77 + tracing::info!( 78 + offset = %offset.magenta(), 79 + end = %((offset + artists.len() as i64).min(total_artists)).magenta(), 80 + total = %total_artists.magenta(), 81 + "Fetched artists" 82 + ); 83 + for artist in &artists { 84 + tracing::info!(name = %artist.name.cyan(), i = %i.magenta(), total = %total_artists.magenta(), "Inserting artist"); 85 + repo::artist::insert_artist(&dest_pool_clone, artist).await?; 86 + i += 1; 87 + } 88 + } 89 + Ok::<(), Error>(()) 90 + }); 91 + 92 + let pool_clone = pool.clone(); 93 + let dest_pool_clone = dest_pool.clone(); 94 + let track_sync = tokio::spawn(async move { 95 + let total_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM tracks") 96 + .fetch_one(&pool_clone) 97 + .await?; 98 + let total_tracks = total_tracks.0; 99 + tracing::info!(total = %total_tracks.magenta(), "Total tracks to sync"); 100 + 101 + const BATCH_SIZE: usize = 1000; 102 + 103 + let start = 0; 104 + let mut i = 1; 105 + 106 + for offset in (start..total_tracks).step_by(BATCH_SIZE) { 107 + let tracks = 108 + repo::track::get_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64).await?; 109 + tracing::info!( 110 + offset = %offset.magenta(), 111 + end = %((offset + tracks.len() as i64).min(total_tracks)).magenta(), 112 + total = %total_tracks.magenta(), 113 + "Fetched tracks" 114 + ); 115 + 116 + for track in &tracks { 117 + tracing::info!(title = %track.title.cyan(), i = %i.magenta(), total = %total_tracks.magenta(), "Inserting track"); 118 + match repo::track::insert_track(&dest_pool_clone, track).await { 119 + Ok(_) => {} 120 + Err(e) => { 121 + tracing::error!(error = %e, "Failed to insert track"); 122 + } 123 + } 124 + i += 1; 125 + } 126 + } 127 + Ok::<(), Error>(()) 128 + }); 129 + 130 + let pool_clone = pool.clone(); 131 + let dest_pool_clone = dest_pool.clone(); 132 + let user_sync = tokio::spawn(async move { 133 + let total_users: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users") 134 + .fetch_one(&pool_clone) 135 + .await?; 136 + let total_users = total_users.0; 137 + tracing::info!(total = %total_users.magenta(), "Total users to sync"); 138 + 139 + const BATCH_SIZE: usize = 1000; 140 + 141 + let start = 0; 142 + let mut i = 1; 143 + 144 + for offset in (start..total_users).step_by(BATCH_SIZE) { 145 + let users = 146 + repo::user::get_users(&pool_clone, offset as i64, BATCH_SIZE as i64).await?; 147 + tracing::info!( 148 + offset = %offset.magenta(), 149 + end = %((offset + users.len() as i64).min(total_users)).magenta(), 150 + total = %total_users.magenta(), 151 + "Fetched users" 152 + ); 153 + 154 + for user in &users { 155 + tracing::info!(handle = %user.handle.cyan(), i = %i.magenta(), total = %total_users.magenta(), "Inserting user"); 156 + match repo::user::insert_user(&dest_pool_clone, user).await { 157 + Ok(_) => {} 158 + Err(e) => { 159 + tracing::error!(error = %e, "Failed to insert user"); 160 + } 161 + } 162 + i += 1; 163 + } 164 + } 165 + Ok::<(), Error>(()) 166 + }); 167 + 168 + let (album_sync, artist_sync, track_sync, user_sync) = 169 + tokio::join!(album_sync, artist_sync, track_sync, user_sync); 170 + 171 + album_sync.context("Album sync task failed")??; 172 + artist_sync.context("Artist sync task failed")??; 173 + track_sync.context("Track sync task failed")??; 174 + user_sync.context("User sync task failed")??; 175 + 176 + let pool_clone = pool.clone(); 177 + let dest_pool_clone = dest_pool.clone(); 178 + let playlist_sync = tokio::spawn(async move { 179 + let total_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlists") 180 + .fetch_one(&pool_clone) 181 + .await?; 182 + let total_playlists = total_playlists.0; 183 + tracing::info!(total = %total_playlists.magenta(), "Total playlists to sync"); 184 + 185 + const BATCH_SIZE: usize = 1000; 186 + 187 + let start = 0; 188 + let mut i = 1; 189 + 190 + for offset in (start..total_playlists).step_by(BATCH_SIZE) { 191 + let playlists = 192 + repo::playlist::get_playlists(&pool_clone, offset as i64, BATCH_SIZE as i64) 193 + .await?; 194 + tracing::info!( 195 + offset = %offset.magenta(), 196 + end = %((offset + playlists.len() as i64).min(total_playlists)).magenta(), 197 + total = %total_playlists.magenta(), 198 + "Fetched playlists" 199 + ); 200 + 201 + for playlist in &playlists { 202 + tracing::info!(name = %playlist.name.cyan(), i = %i.magenta(), total = %total_playlists.magenta(), "Inserting playlist"); 203 + match repo::playlist::insert_playlist(&dest_pool_clone, playlist).await { 204 + Ok(_) => {} 205 + Err(e) => { 206 + tracing::error!(error = %e, "Failed to insert playlist"); 207 + } 208 + } 209 + i += 1; 210 + } 211 + } 212 + Ok::<(), Error>(()) 213 + }); 214 + 215 + let pool_clone = pool.clone(); 216 + let dest_pool_clone = dest_pool.clone(); 217 + let loved_track_sync = tokio::spawn(async move { 218 + let total_loved_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM loved_tracks") 219 + .fetch_one(&pool_clone) 220 + .await?; 221 + let total_loved_tracks = total_loved_tracks.0; 222 + tracing::info!(total = %total_loved_tracks.magenta(), "Total loved tracks to sync"); 223 + 224 + const BATCH_SIZE: usize = 1000; 225 + 226 + let start = 0; 227 + let mut i = 1; 228 + 229 + for offset in (start..total_loved_tracks).step_by(BATCH_SIZE) { 230 + let loved_tracks = 231 + repo::loved_track::get_loved_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64) 232 + .await?; 233 + tracing::info!( 234 + offset = %offset.magenta(), 235 + end = %((offset + loved_tracks.len() as i64).min(total_loved_tracks)).magenta(), 236 + total = %total_loved_tracks.magenta(), 237 + "Fetched loved tracks" 238 + ); 239 + 240 + for loved_track in &loved_tracks { 241 + tracing::info!(user_id = %loved_track.user_id.cyan(), track_id = %loved_track.track_id.magenta(), i = %i.magenta(), total = %total_loved_tracks.magenta(), "Inserting loved track"); 242 + repo::loved_track::insert_loved_track(&dest_pool_clone, loved_track).await?; 243 + i += 1; 244 + } 245 + } 246 + Ok::<(), Error>(()) 247 + }); 248 + 249 + let pool_clone = pool.clone(); 250 + let dest_pool_clone = dest_pool.clone(); 251 + 252 + let scrobble_sync = tokio::spawn(async move { 253 + let total_scrobbles: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM scrobbles") 254 + .fetch_one(&pool_clone) 255 + .await?; 256 + let total_scrobbles = total_scrobbles.0; 257 + tracing::info!(total = %total_scrobbles.magenta(), "Total scrobbles to sync"); 258 + 259 + const BATCH_SIZE: usize = 1000; 260 + 261 + let start = 0; 262 + let mut i = 1; 263 + 264 + for offset in (start..total_scrobbles).step_by(BATCH_SIZE) { 265 + let scrobbles = 266 + repo::scrobble::get_scrobbles(&pool_clone, offset as i64, BATCH_SIZE as i64) 267 + .await?; 268 + tracing::info!( 269 + offset = %offset.magenta(), 270 + end = %((offset + scrobbles.len() as i64).min(total_scrobbles)).magenta(), 271 + total = %total_scrobbles.magenta(), 272 + "Fetched scrobbles" 273 + ); 274 + 275 + for scrobble in &scrobbles { 276 + tracing::info!(user_id = %scrobble.user_id.cyan(), track_id = %scrobble.track_id.magenta(), i = %i.magenta(), total = %total_scrobbles.magenta(), "Inserting scrobble"); 277 + match repo::scrobble::insert_scrobble(&dest_pool_clone, scrobble).await { 278 + Ok(_) => {} 279 + Err(e) => { 280 + tracing::error!(error = %e, "Failed to insert scrobble"); 281 + } 282 + } 283 + i += 1; 284 + } 285 + } 286 + Ok::<(), Error>(()) 287 + }); 288 + 289 + let (loved_track_sync, playlist_sync, scrobble_sync) = 290 + tokio::join!(loved_track_sync, playlist_sync, scrobble_sync); 291 + loved_track_sync.context("Loved track sync task failed")??; 292 + playlist_sync.context("Playlist sync task failed")??; 293 + scrobble_sync.context("Scrobble sync task failed")??; 294 + 18 295 Ok(()) 19 296 }
+57
crates/pgpull/src/repo/album.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::album::Album; 5 + 6 + pub async fn get_albums( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Album>, Error> { 11 + let albums: Vec<Album> = sqlx::query_as("SELECT * FROM albums OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(albums) 17 + } 18 + 19 + pub async fn insert_album(pool: &Pool<Postgres>, album: &Album) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO albums ( 22 + xata_id, 23 + title, 24 + artist, 25 + release_date, 26 + album_art, 27 + year, 28 + spotify_link, 29 + tidal_link, 30 + youtube_link, 31 + apple_music_link, 32 + sha256, 33 + uri, 34 + artist_uri, 35 + xata_createdat 36 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) 37 + ON CONFLICT (xata_id) DO NOTHING"#, 38 + ) 39 + .bind(&album.xata_id) 40 + .bind(&album.title) 41 + .bind(&album.artist) 42 + .bind(&album.release_date) 43 + .bind(&album.album_art) 44 + .bind(album.year) 45 + .bind(&album.spotify_link) 46 + .bind(&album.tidal_link) 47 + .bind(&album.youtube_link) 48 + .bind(&album.apple_music_link) 49 + .bind(&album.sha256) 50 + .bind(&album.uri) 51 + .bind(&album.artist_uri) 52 + .bind(album.xata_createdat) 53 + .execute(pool) 54 + .await?; 55 + 56 + Ok(()) 57 + }
+58
crates/pgpull/src/repo/artist.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::artist::Artist; 5 + 6 + pub async fn get_artists( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Artist>, Error> { 11 + let artists = sqlx::query_as::<_, Artist>("SELECT * FROM artists OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(artists) 17 + } 18 + 19 + pub async fn insert_artist(pool: &Pool<Postgres>, artist: &Artist) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO artists ( 22 + xata_id, 23 + name, 24 + biography, 25 + born, 26 + born_in, 27 + died, 28 + picture, 29 + sha256, 30 + spotify_link, 31 + tidal_link, 32 + youtube_link, 33 + apple_music_link, 34 + uri, 35 + genres, 36 + xata_createdat 37 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) 38 + ON CONFLICT (xata_id) DO NOTHING"#, 39 + ) 40 + .bind(&artist.xata_id) 41 + .bind(&artist.name) 42 + .bind(&artist.biography) 43 + .bind(&artist.born) 44 + .bind(&artist.born_in) 45 + .bind(&artist.died) 46 + .bind(&artist.picture) 47 + .bind(&artist.sha256) 48 + .bind(&artist.spotify_link) 49 + .bind(&artist.tidal_link) 50 + .bind(&artist.youtube_link) 51 + .bind(&artist.apple_music_link) 52 + .bind(&artist.uri) 53 + .bind(&artist.genres) 54 + .bind(artist.xata_createdat) 55 + .execute(pool) 56 + .await?; 57 + Ok(()) 58 + }
+40
crates/pgpull/src/repo/loved_track.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::loved_track::LovedTrack; 5 + 6 + pub async fn get_loved_tracks( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<LovedTrack>, Error> { 11 + let loved_tracks = 12 + sqlx::query_as::<_, LovedTrack>("SELECT * FROM loved_tracks OFFSET $1 LIMIT $2") 13 + .bind(offset) 14 + .bind(limit) 15 + .fetch_all(pool) 16 + .await?; 17 + Ok(loved_tracks) 18 + } 19 + 20 + pub async fn insert_loved_track( 21 + pool: &Pool<Postgres>, 22 + loved_track: &LovedTrack, 23 + ) -> Result<(), Error> { 24 + sqlx::query( 25 + r#"INSERT INTO loved_tracks ( 26 + xata_id, 27 + user_id, 28 + track_id, 29 + xata_createdat 30 + ) VALUES ($1, $2, $3, $4) 31 + ON CONFLICT (xata_id) DO NOTHING"#, 32 + ) 33 + .bind(&loved_track.xata_id) 34 + .bind(&loved_track.user_id) 35 + .bind(&loved_track.track_id) 36 + .bind(loved_track.xata_createdat) 37 + .execute(pool) 38 + .await?; 39 + Ok(()) 40 + }
+1
crates/pgpull/src/repo/mod.rs
··· 1 1 pub mod album; 2 2 pub mod artist; 3 + pub mod loved_track; 3 4 pub mod playlist; 4 5 pub mod scrobble; 5 6 pub mod track;
+50
crates/pgpull/src/repo/playlist.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::playlist::Playlist; 5 + 6 + pub async fn get_playlists( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Playlist>, Error> { 11 + let playlists: Vec<Playlist> = sqlx::query_as("SELECT * FROM playlists OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(playlists) 17 + } 18 + 19 + pub async fn insert_playlist(pool: &Pool<Postgres>, playlist: &Playlist) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO playlists ( 22 + xata_id, 23 + name, 24 + description, 25 + picture, 26 + spotify_link, 27 + tidal_link, 28 + apple_music_link, 29 + xata_createdat, 30 + xata_updatedat, 31 + uri, 32 + created_by 33 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) 34 + ON CONFLICT (xata_id) DO NOTHING"#, 35 + ) 36 + .bind(&playlist.xata_id) 37 + .bind(&playlist.name) 38 + .bind(&playlist.description) 39 + .bind(&playlist.picture) 40 + .bind(&playlist.spotify_link) 41 + .bind(&playlist.tidal_link) 42 + .bind(&playlist.apple_music_link) 43 + .bind(playlist.xata_createdat) 44 + .bind(playlist.xata_updatedat) 45 + .bind(&playlist.uri) 46 + .bind(&playlist.created_by) 47 + .execute(pool) 48 + .await?; 49 + Ok(()) 50 + }
+44
crates/pgpull/src/repo/scrobble.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::scrobble::Scrobble; 5 + 6 + pub async fn get_scrobbles( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Scrobble>, Error> { 11 + let scrobbles = sqlx::query_as::<_, Scrobble>("SELECT * FROM scrobbles OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(scrobbles) 17 + } 18 + 19 + pub async fn insert_scrobble(pool: &Pool<Postgres>, scrobble: &Scrobble) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO scrobbles ( 22 + xata_id, 23 + user_id, 24 + track_id, 25 + album_id, 26 + artist_id, 27 + uri, 28 + xata_createdat, 29 + timestamp 30 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) 31 + ON CONFLICT (xata_id) DO NOTHING"#, 32 + ) 33 + .bind(&scrobble.xata_id) 34 + .bind(&scrobble.user_id) 35 + .bind(&scrobble.track_id) 36 + .bind(&scrobble.album_id) 37 + .bind(&scrobble.artist_id) 38 + .bind(&scrobble.uri) 39 + .bind(scrobble.xata_createdat) 40 + .bind(scrobble.timestamp) 41 + .execute(pool) 42 + .await?; 43 + Ok(()) 44 + }
+77
crates/pgpull/src/repo/track.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::track::Track; 5 + 6 + pub async fn get_tracks( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Track>, Error> { 11 + let tracks: Vec<Track> = sqlx::query_as("SELECT * FROM tracks OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(tracks) 17 + } 18 + 19 + pub async fn insert_track(pool: &Pool<Postgres>, track: &Track) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO tracks ( 22 + xata_id, 23 + title, 24 + artist, 25 + album_artist, 26 + album_art, 27 + album, 28 + track_number, 29 + duration, 30 + mb_id, 31 + youtube_link, 32 + spotify_link, 33 + tidal_link, 34 + apple_music_link, 35 + sha256, 36 + lyrics, 37 + composer, 38 + genre, 39 + disc_number, 40 + copyright_message, 41 + label, 42 + uri, 43 + artist_uri, 44 + album_uri, 45 + xata_createdat 46 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24) 47 + ON CONFLICT (xata_id) DO NOTHING 48 + "#, 49 + ) 50 + .bind(&track.xata_id) 51 + .bind(&track.title) 52 + .bind(&track.artist) 53 + .bind(&track.album_artist) 54 + .bind(&track.album_art) 55 + .bind(&track.album) 56 + .bind(track.track_number) 57 + .bind(track.duration) 58 + .bind(&track.mb_id) 59 + .bind(&track.youtube_link) 60 + .bind(&track.spotify_link) 61 + .bind(&track.tidal_link) 62 + .bind(&track.apple_music_link) 63 + .bind(&track.sha256) 64 + .bind(&track.lyrics) 65 + .bind(&track.composer) 66 + .bind(&track.genre) 67 + .bind(track.disc_number) 68 + .bind(&track.copyright_message) 69 + .bind(&track.label) 70 + .bind(&track.uri) 71 + .bind(&track.artist_uri) 72 + .bind(&track.album_uri) 73 + .bind(track.xata_createdat) 74 + .execute(pool) 75 + .await?; 76 + Ok(()) 77 + }
+36
crates/pgpull/src/repo/user.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::user::User; 5 + 6 + pub async fn get_users(pool: &Pool<Postgres>, offset: i64, limit: i64) -> Result<Vec<User>, Error> { 7 + let users = sqlx::query_as::<_, User>("SELECT * FROM users OFFSET $1 LIMIT $2") 8 + .bind(offset) 9 + .bind(limit) 10 + .fetch_all(pool) 11 + .await?; 12 + Ok(users) 13 + } 14 + 15 + pub async fn insert_user(pool: &Pool<Postgres>, user: &User) -> Result<(), Error> { 16 + sqlx::query( 17 + r#"INSERT INTO users ( 18 + xata_id, 19 + display_name, 20 + did, 21 + handle, 22 + avatar, 23 + xata_createdat 24 + ) VALUES ($1, $2, $3, $4, $5, $6) 25 + ON CONFLICT (xata_id) DO NOTHING"#, 26 + ) 27 + .bind(&user.xata_id) 28 + .bind(&user.display_name) 29 + .bind(&user.did) 30 + .bind(&user.handle) 31 + .bind(&user.avatar) 32 + .bind(user.xata_createdat) 33 + .execute(pool) 34 + .await?; 35 + Ok(()) 36 + }