tangled
alpha
login
or
join now
t1c.dev
/
rocksky
forked from
rocksky.app/rocksky
2
fork
atom
A decentralized music tracking and discovery platform built on AT Protocol 🎵
2
fork
atom
overview
issues
pulls
pipelines
refactors pull_data function
tsiry-sandratraina.com
5 months ago
5a329b42
692a1fa2
+515
-482
1 changed file
expand all
collapse all
unified
split
crates
pgpull
src
lib.rs
+515
-482
crates/pgpull/src/lib.rs
···
5
5
6
6
use anyhow::{Context, Error};
7
7
use owo_colors::OwoColorize;
8
8
-
use sqlx::postgres::PgPoolOptions;
8
8
+
use sqlx::{postgres::PgPoolOptions, PgPool};
9
9
10
10
-
pub async fn pull_data() -> Result<(), Error> {
10
10
+
const MAX_CONNECTIONS: u32 = 5;
11
11
+
const BATCH_SIZE: usize = 1000;
12
12
+
13
13
+
#[derive(Clone)]
14
14
+
pub struct DatabasePools {
15
15
+
pub source: PgPool,
16
16
+
pub destination: PgPool,
17
17
+
}
18
18
+
19
19
+
async fn setup_database_pools() -> Result<DatabasePools, Error> {
11
20
if env::var("SOURCE_POSTGRES_URL").is_err() {
12
21
tracing::error!(
13
22
"SOURCE_POSTGRES_URL is not set. Please set it to your PostgreSQL connection string."
···
15
24
std::process::exit(1);
16
25
}
17
26
18
18
-
let pool = PgPoolOptions::new()
19
19
-
.max_connections(5)
27
27
+
let source = PgPoolOptions::new()
28
28
+
.max_connections(MAX_CONNECTIONS)
20
29
.connect(&env::var("SOURCE_POSTGRES_URL")?)
21
30
.await?;
22
31
23
23
-
let dest_pool = PgPoolOptions::new()
24
24
-
.max_connections(5)
32
32
+
let destination = PgPoolOptions::new()
33
33
+
.max_connections(MAX_CONNECTIONS)
25
34
.connect(&env::var("XATA_POSTGRES_URL")?)
26
35
.await?;
27
36
28
28
-
let pool_clone = pool.clone();
29
29
-
let dest_pool_clone = dest_pool.clone();
30
30
-
let album_sync = tokio::spawn(async move {
31
31
-
let total_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM albums")
32
32
-
.fetch_one(&pool_clone)
33
33
-
.await?;
34
34
-
let total_albums = total_albums.0;
35
35
-
tracing::info!(total = %total_albums.magenta(), "Total albums to sync");
37
37
+
Ok(DatabasePools {
38
38
+
source,
39
39
+
destination,
40
40
+
})
41
41
+
}
36
42
37
37
-
const BATCH_SIZE: usize = 1000;
43
43
+
async fn sync_albums(pools: &DatabasePools) -> Result<(), Error> {
44
44
+
let total_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM albums")
45
45
+
.fetch_one(&pools.source)
46
46
+
.await?;
47
47
+
let total_albums = total_albums.0;
48
48
+
tracing::info!(total = %total_albums.magenta(), "Total albums to sync");
38
49
39
39
-
let start = 0;
40
40
-
let mut i = 1;
50
50
+
let start = 0;
51
51
+
let mut i = 1;
41
52
42
42
-
for offset in (start..total_albums).step_by(BATCH_SIZE) {
43
43
-
let albums =
44
44
-
repo::album::get_albums(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
45
45
-
tracing::info!(
46
46
-
offset = %offset.magenta(),
47
47
-
end = %((offset + albums.len() as i64).min(total_albums)).magenta(),
48
48
-
total = %total_albums.magenta(),
49
49
-
"Fetched albums"
50
50
-
);
51
51
-
for album in &albums {
52
52
-
tracing::info!(title = %album.title.cyan(), i = %i.magenta(), total = %total_albums.magenta(), "Inserting album");
53
53
-
repo::album::insert_album(&dest_pool_clone, album).await?;
54
54
-
i += 1;
55
55
-
}
53
53
+
for offset in (start..total_albums).step_by(BATCH_SIZE) {
54
54
+
let albums =
55
55
+
repo::album::get_albums(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
56
56
+
tracing::info!(
57
57
+
offset = %offset.magenta(),
58
58
+
end = %((offset + albums.len() as i64).min(total_albums)).magenta(),
59
59
+
total = %total_albums.magenta(),
60
60
+
"Fetched albums"
61
61
+
);
62
62
+
for album in &albums {
63
63
+
tracing::info!(title = %album.title.cyan(), i = %i.magenta(), total = %total_albums.magenta(), "Inserting album");
64
64
+
repo::album::insert_album(&pools.destination, album).await?;
65
65
+
i += 1;
56
66
}
57
57
-
Ok::<(), Error>(())
58
58
-
});
67
67
+
}
68
68
+
Ok(())
69
69
+
}
59
70
60
60
-
let pool_clone = pool.clone();
61
61
-
let dest_pool_clone = dest_pool.clone();
62
62
-
let artist_sync = tokio::spawn(async move {
63
63
-
let total_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artists")
64
64
-
.fetch_one(&pool_clone)
65
65
-
.await?;
66
66
-
let total_artists = total_artists.0;
67
67
-
tracing::info!(total = %total_artists.magenta(), "Total artists to sync");
71
71
+
async fn sync_artists(pools: &DatabasePools) -> Result<(), Error> {
72
72
+
let total_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artists")
73
73
+
.fetch_one(&pools.source)
74
74
+
.await?;
75
75
+
let total_artists = total_artists.0;
76
76
+
tracing::info!(total = %total_artists.magenta(), "Total artists to sync");
68
77
69
69
-
const BATCH_SIZE: usize = 1000;
78
78
+
let start = 0;
79
79
+
let mut i = 1;
70
80
71
71
-
let start = 0;
72
72
-
let mut i = 1;
81
81
+
for offset in (start..total_artists).step_by(BATCH_SIZE) {
82
82
+
let artists =
83
83
+
repo::artist::get_artists(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
84
84
+
tracing::info!(
85
85
+
offset = %offset.magenta(),
86
86
+
end = %((offset + artists.len() as i64).min(total_artists)).magenta(),
87
87
+
total = %total_artists.magenta(),
88
88
+
"Fetched artists"
89
89
+
);
90
90
+
for artist in &artists {
91
91
+
tracing::info!(name = %artist.name.cyan(), i = %i.magenta(), total = %total_artists.magenta(), "Inserting artist");
92
92
+
repo::artist::insert_artist(&pools.destination, artist).await?;
93
93
+
i += 1;
94
94
+
}
95
95
+
}
96
96
+
Ok(())
97
97
+
}
73
98
74
74
-
for offset in (start..total_artists).step_by(BATCH_SIZE) {
75
75
-
let artists =
76
76
-
repo::artist::get_artists(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
77
77
-
tracing::info!(
78
78
-
offset = %offset.magenta(),
79
79
-
end = %((offset + artists.len() as i64).min(total_artists)).magenta(),
80
80
-
total = %total_artists.magenta(),
81
81
-
"Fetched artists"
82
82
-
);
83
83
-
for artist in &artists {
84
84
-
tracing::info!(name = %artist.name.cyan(), i = %i.magenta(), total = %total_artists.magenta(), "Inserting artist");
85
85
-
repo::artist::insert_artist(&dest_pool_clone, artist).await?;
86
86
-
i += 1;
99
99
+
async fn sync_tracks(pools: &DatabasePools) -> Result<(), Error> {
100
100
+
let total_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM tracks")
101
101
+
.fetch_one(&pools.source)
102
102
+
.await?;
103
103
+
let total_tracks = total_tracks.0;
104
104
+
tracing::info!(total = %total_tracks.magenta(), "Total tracks to sync");
105
105
+
106
106
+
let start = 0;
107
107
+
let mut i = 1;
108
108
+
109
109
+
for offset in (start..total_tracks).step_by(BATCH_SIZE) {
110
110
+
let tracks =
111
111
+
repo::track::get_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
112
112
+
tracing::info!(
113
113
+
offset = %offset.magenta(),
114
114
+
end = %((offset + tracks.len() as i64).min(total_tracks)).magenta(),
115
115
+
total = %total_tracks.magenta(),
116
116
+
"Fetched tracks"
117
117
+
);
118
118
+
119
119
+
for track in &tracks {
120
120
+
tracing::info!(title = %track.title.cyan(), i = %i.magenta(), total = %total_tracks.magenta(), "Inserting track");
121
121
+
match repo::track::insert_track(&pools.destination, track).await {
122
122
+
Ok(_) => {}
123
123
+
Err(e) => {
124
124
+
tracing::error!(error = %e, "Failed to insert track");
125
125
+
}
87
126
}
127
127
+
i += 1;
88
128
}
89
89
-
Ok::<(), Error>(())
90
90
-
});
129
129
+
}
130
130
+
Ok(())
131
131
+
}
91
132
92
92
-
let pool_clone = pool.clone();
93
93
-
let dest_pool_clone = dest_pool.clone();
94
94
-
let track_sync = tokio::spawn(async move {
95
95
-
let total_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM tracks")
96
96
-
.fetch_one(&pool_clone)
97
97
-
.await?;
98
98
-
let total_tracks = total_tracks.0;
99
99
-
tracing::info!(total = %total_tracks.magenta(), "Total tracks to sync");
133
133
+
async fn sync_users(pools: &DatabasePools) -> Result<(), Error> {
134
134
+
let total_users: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
135
135
+
.fetch_one(&pools.source)
136
136
+
.await?;
137
137
+
let total_users = total_users.0;
138
138
+
tracing::info!(total = %total_users.magenta(), "Total users to sync");
100
139
101
101
-
const BATCH_SIZE: usize = 1000;
140
140
+
let start = 0;
141
141
+
let mut i = 1;
102
142
103
103
-
let start = 0;
104
104
-
let mut i = 1;
143
143
+
for offset in (start..total_users).step_by(BATCH_SIZE) {
144
144
+
let users = repo::user::get_users(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
145
145
+
tracing::info!(
146
146
+
offset = %offset.magenta(),
147
147
+
end = %((offset + users.len() as i64).min(total_users)).magenta(),
148
148
+
total = %total_users.magenta(),
149
149
+
"Fetched users"
150
150
+
);
105
151
106
106
-
for offset in (start..total_tracks).step_by(BATCH_SIZE) {
107
107
-
let tracks =
108
108
-
repo::track::get_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
109
109
-
tracing::info!(
110
110
-
offset = %offset.magenta(),
111
111
-
end = %((offset + tracks.len() as i64).min(total_tracks)).magenta(),
112
112
-
total = %total_tracks.magenta(),
113
113
-
"Fetched tracks"
114
114
-
);
115
115
-
116
116
-
for track in &tracks {
117
117
-
tracing::info!(title = %track.title.cyan(), i = %i.magenta(), total = %total_tracks.magenta(), "Inserting track");
118
118
-
match repo::track::insert_track(&dest_pool_clone, track).await {
119
119
-
Ok(_) => {}
120
120
-
Err(e) => {
121
121
-
tracing::error!(error = %e, "Failed to insert track");
122
122
-
}
152
152
+
for user in &users {
153
153
+
tracing::info!(handle = %user.handle.cyan(), i = %i.magenta(), total = %total_users.magenta(), "Inserting user");
154
154
+
match repo::user::insert_user(&pools.destination, user).await {
155
155
+
Ok(_) => {}
156
156
+
Err(e) => {
157
157
+
tracing::error!(error = %e, "Failed to insert user");
123
158
}
124
124
-
i += 1;
125
159
}
160
160
+
i += 1;
126
161
}
127
127
-
Ok::<(), Error>(())
128
128
-
});
162
162
+
}
163
163
+
Ok(())
164
164
+
}
129
165
130
130
-
let pool_clone = pool.clone();
131
131
-
let dest_pool_clone = dest_pool.clone();
132
132
-
let user_sync = tokio::spawn(async move {
133
133
-
let total_users: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
134
134
-
.fetch_one(&pool_clone)
135
135
-
.await?;
136
136
-
let total_users = total_users.0;
137
137
-
tracing::info!(total = %total_users.magenta(), "Total users to sync");
166
166
+
async fn sync_playlists(pools: &DatabasePools) -> Result<(), Error> {
167
167
+
let total_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlists")
168
168
+
.fetch_one(&pools.source)
169
169
+
.await?;
170
170
+
let total_playlists = total_playlists.0;
171
171
+
tracing::info!(total = %total_playlists.magenta(), "Total playlists to sync");
138
172
139
139
-
const BATCH_SIZE: usize = 1000;
173
173
+
let start = 0;
174
174
+
let mut i = 1;
140
175
141
141
-
let start = 0;
142
142
-
let mut i = 1;
176
176
+
for offset in (start..total_playlists).step_by(BATCH_SIZE) {
177
177
+
let playlists =
178
178
+
repo::playlist::get_playlists(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
179
179
+
tracing::info!(
180
180
+
offset = %offset.magenta(),
181
181
+
end = %((offset + playlists.len() as i64).min(total_playlists)).magenta(),
182
182
+
total = %total_playlists.magenta(),
183
183
+
"Fetched playlists"
184
184
+
);
143
185
144
144
-
for offset in (start..total_users).step_by(BATCH_SIZE) {
145
145
-
let users =
146
146
-
repo::user::get_users(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
147
147
-
tracing::info!(
148
148
-
offset = %offset.magenta(),
149
149
-
end = %((offset + users.len() as i64).min(total_users)).magenta(),
150
150
-
total = %total_users.magenta(),
151
151
-
"Fetched users"
152
152
-
);
153
153
-
154
154
-
for user in &users {
155
155
-
tracing::info!(handle = %user.handle.cyan(), i = %i.magenta(), total = %total_users.magenta(), "Inserting user");
156
156
-
match repo::user::insert_user(&dest_pool_clone, user).await {
157
157
-
Ok(_) => {}
158
158
-
Err(e) => {
159
159
-
tracing::error!(error = %e, "Failed to insert user");
160
160
-
}
186
186
+
for playlist in &playlists {
187
187
+
tracing::info!(name = %playlist.name.cyan(), i = %i.magenta(), total = %total_playlists.magenta(), "Inserting playlist");
188
188
+
match repo::playlist::insert_playlist(&pools.destination, playlist).await {
189
189
+
Ok(_) => {}
190
190
+
Err(e) => {
191
191
+
tracing::error!(error = %e, "Failed to insert playlist");
161
192
}
162
162
-
i += 1;
163
193
}
194
194
+
i += 1;
164
195
}
165
165
-
Ok::<(), Error>(())
166
166
-
});
196
196
+
}
197
197
+
Ok(())
198
198
+
}
167
199
168
168
-
let (album_sync, artist_sync, track_sync, user_sync) =
169
169
-
tokio::join!(album_sync, artist_sync, track_sync, user_sync);
200
200
+
async fn sync_loved_tracks(pools: &DatabasePools) -> Result<(), Error> {
201
201
+
let total_loved_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM loved_tracks")
202
202
+
.fetch_one(&pools.source)
203
203
+
.await?;
204
204
+
let total_loved_tracks = total_loved_tracks.0;
205
205
+
tracing::info!(total = %total_loved_tracks.magenta(), "Total loved tracks to sync");
206
206
+
207
207
+
let start = 0;
208
208
+
let mut i = 1;
170
209
171
171
-
album_sync.context("Album sync task failed")??;
172
172
-
artist_sync.context("Artist sync task failed")??;
173
173
-
track_sync.context("Track sync task failed")??;
174
174
-
user_sync.context("User sync task failed")??;
210
210
+
for offset in (start..total_loved_tracks).step_by(BATCH_SIZE) {
211
211
+
let loved_tracks =
212
212
+
repo::loved_track::get_loved_tracks(&pools.source, offset as i64, BATCH_SIZE as i64)
213
213
+
.await?;
214
214
+
tracing::info!(
215
215
+
offset = %offset.magenta(),
216
216
+
end = %((offset + loved_tracks.len() as i64).min(total_loved_tracks)).magenta(),
217
217
+
total = %total_loved_tracks.magenta(),
218
218
+
"Fetched loved tracks"
219
219
+
);
175
220
176
176
-
let pool_clone = pool.clone();
177
177
-
let dest_pool_clone = dest_pool.clone();
178
178
-
let playlist_sync = tokio::spawn(async move {
179
179
-
let total_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlists")
180
180
-
.fetch_one(&pool_clone)
181
181
-
.await?;
182
182
-
let total_playlists = total_playlists.0;
183
183
-
tracing::info!(total = %total_playlists.magenta(), "Total playlists to sync");
221
221
+
for loved_track in &loved_tracks {
222
222
+
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");
223
223
+
match repo::loved_track::insert_loved_track(&pools.destination, loved_track).await {
224
224
+
Ok(_) => {}
225
225
+
Err(e) => {
226
226
+
tracing::error!(error = %e, "Failed to insert loved track");
227
227
+
}
228
228
+
}
229
229
+
i += 1;
230
230
+
}
231
231
+
}
232
232
+
Ok(())
233
233
+
}
184
234
185
185
-
const BATCH_SIZE: usize = 1000;
235
235
+
async fn sync_scrobbles(pools: &DatabasePools) -> Result<(), Error> {
236
236
+
let total_scrobbles: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM scrobbles")
237
237
+
.fetch_one(&pools.source)
238
238
+
.await?;
239
239
+
let total_scrobbles = total_scrobbles.0;
240
240
+
tracing::info!(total = %total_scrobbles.magenta(), "Total scrobbles to sync");
186
241
187
187
-
let start = 0;
188
188
-
let mut i = 1;
242
242
+
let start = 0;
243
243
+
let mut i = 1;
189
244
190
190
-
for offset in (start..total_playlists).step_by(BATCH_SIZE) {
191
191
-
let playlists =
192
192
-
repo::playlist::get_playlists(&pool_clone, offset as i64, BATCH_SIZE as i64)
193
193
-
.await?;
194
194
-
tracing::info!(
195
195
-
offset = %offset.magenta(),
196
196
-
end = %((offset + playlists.len() as i64).min(total_playlists)).magenta(),
197
197
-
total = %total_playlists.magenta(),
198
198
-
"Fetched playlists"
199
199
-
);
245
245
+
for offset in (start..total_scrobbles).step_by(BATCH_SIZE) {
246
246
+
let scrobbles =
247
247
+
repo::scrobble::get_scrobbles(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
248
248
+
tracing::info!(
249
249
+
offset = %offset.magenta(),
250
250
+
end = %((offset + scrobbles.len() as i64).min(total_scrobbles)).magenta(),
251
251
+
total = %total_scrobbles.magenta(),
252
252
+
"Fetched scrobbles"
253
253
+
);
200
254
201
201
-
for playlist in &playlists {
202
202
-
tracing::info!(name = %playlist.name.cyan(), i = %i.magenta(), total = %total_playlists.magenta(), "Inserting playlist");
203
203
-
match repo::playlist::insert_playlist(&dest_pool_clone, playlist).await {
204
204
-
Ok(_) => {}
205
205
-
Err(e) => {
206
206
-
tracing::error!(error = %e, "Failed to insert playlist");
207
207
-
}
255
255
+
for scrobble in &scrobbles {
256
256
+
tracing::info!(user_id = %scrobble.user_id.cyan(), track_id = %scrobble.track_id.magenta(), i = %i.magenta(), total = %total_scrobbles.magenta(), "Inserting scrobble");
257
257
+
match repo::scrobble::insert_scrobble(&pools.destination, scrobble).await {
258
258
+
Ok(_) => {}
259
259
+
Err(e) => {
260
260
+
tracing::error!(error = %e, "Failed to insert scrobble");
208
261
}
209
209
-
i += 1;
210
262
}
263
263
+
i += 1;
211
264
}
212
212
-
Ok::<(), Error>(())
213
213
-
});
265
265
+
}
266
266
+
Ok(())
267
267
+
}
214
268
215
215
-
let pool_clone = pool.clone();
216
216
-
let dest_pool_clone = dest_pool.clone();
217
217
-
let loved_track_sync = tokio::spawn(async move {
218
218
-
let total_loved_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM loved_tracks")
219
219
-
.fetch_one(&pool_clone)
220
220
-
.await?;
221
221
-
let total_loved_tracks = total_loved_tracks.0;
222
222
-
tracing::info!(total = %total_loved_tracks.magenta(), "Total loved tracks to sync");
269
269
+
async fn sync_album_tracks(pools: &DatabasePools) -> Result<(), Error> {
270
270
+
let total_album_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM album_tracks")
271
271
+
.fetch_one(&pools.source)
272
272
+
.await?;
273
273
+
let total_album_tracks = total_album_tracks.0;
274
274
+
tracing::info!(total = %total_album_tracks.magenta(), "Total album tracks to sync");
223
275
224
224
-
const BATCH_SIZE: usize = 1000;
276
276
+
let start = 0;
277
277
+
let mut i = 1;
225
278
226
226
-
let start = 0;
227
227
-
let mut i = 1;
228
228
-
229
229
-
for offset in (start..total_loved_tracks).step_by(BATCH_SIZE) {
230
230
-
let loved_tracks =
231
231
-
repo::loved_track::get_loved_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64)
232
232
-
.await?;
233
233
-
tracing::info!(
234
234
-
offset = %offset.magenta(),
235
235
-
end = %((offset + loved_tracks.len() as i64).min(total_loved_tracks)).magenta(),
236
236
-
total = %total_loved_tracks.magenta(),
237
237
-
"Fetched loved tracks"
238
238
-
);
279
279
+
for offset in (start..total_album_tracks).step_by(BATCH_SIZE) {
280
280
+
let album_tracks =
281
281
+
repo::album::get_album_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
282
282
+
tracing::info!(
283
283
+
offset = %offset.magenta(),
284
284
+
end = %((offset + album_tracks.len() as i64).min(total_album_tracks)).magenta(),
285
285
+
total = %total_album_tracks.magenta(),
286
286
+
"Fetched album tracks"
287
287
+
);
239
288
240
240
-
for loved_track in &loved_tracks {
241
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
242
-
match repo::loved_track::insert_loved_track(&dest_pool_clone, loved_track).await {
243
243
-
Ok(_) => {}
244
244
-
Err(e) => {
245
245
-
tracing::error!(error = %e, "Failed to insert loved track");
246
246
-
}
289
289
+
for album_track in &album_tracks {
290
290
+
tracing::info!(album_id = %album_track.album_id.cyan(), track_id = %album_track.track_id.magenta(), i = %i.magenta(), total = %total_album_tracks.magenta(), "Inserting album track");
291
291
+
match repo::album::insert_album_track(&pools.destination, album_track).await {
292
292
+
Ok(_) => {}
293
293
+
Err(e) => {
294
294
+
tracing::error!(error = %e, "Failed to insert album track");
247
295
}
248
248
-
i += 1;
249
296
}
297
297
+
i += 1;
250
298
}
251
251
-
Ok::<(), Error>(())
252
252
-
});
299
299
+
}
300
300
+
Ok(())
301
301
+
}
253
302
254
254
-
let pool_clone = pool.clone();
255
255
-
let dest_pool_clone = dest_pool.clone();
303
303
+
async fn sync_artist_albums(pools: &DatabasePools) -> Result<(), Error> {
304
304
+
let total_artist_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_albums")
305
305
+
.fetch_one(&pools.source)
306
306
+
.await?;
307
307
+
let total_artist_albums = total_artist_albums.0;
308
308
+
tracing::info!(total = %total_artist_albums.magenta(), "Total artist albums to sync");
256
309
257
257
-
let scrobble_sync = tokio::spawn(async move {
258
258
-
let total_scrobbles: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM scrobbles")
259
259
-
.fetch_one(&pool_clone)
260
260
-
.await?;
261
261
-
let total_scrobbles = total_scrobbles.0;
262
262
-
tracing::info!(total = %total_scrobbles.magenta(), "Total scrobbles to sync");
310
310
+
let start = 0;
311
311
+
let mut i = 1;
263
312
264
264
-
const BATCH_SIZE: usize = 1000;
313
313
+
for offset in (start..total_artist_albums).step_by(BATCH_SIZE) {
314
314
+
let artist_albums =
315
315
+
repo::artist::get_artist_albums(&pools.source, offset as i64, BATCH_SIZE as i64)
316
316
+
.await?;
317
317
+
tracing::info!(
318
318
+
offset = %offset.magenta(),
319
319
+
end = %((offset + artist_albums.len() as i64).min(total_artist_albums)).magenta(),
320
320
+
total = %total_artist_albums.magenta(),
321
321
+
"Fetched artist albums"
322
322
+
);
265
323
266
266
-
let start = 0;
267
267
-
let mut i = 1;
324
324
+
for artist_album in &artist_albums {
325
325
+
tracing::info!(artist_id = %artist_album.artist_id.cyan(), album_id = %artist_album.album_id.magenta(), i = %i.magenta(), total = %total_artist_albums.magenta(), "Inserting artist album");
326
326
+
match repo::artist::insert_artist_album(&pools.destination, artist_album).await {
327
327
+
Ok(_) => {}
328
328
+
Err(e) => {
329
329
+
tracing::error!(error = %e, "Failed to insert artist album");
330
330
+
}
331
331
+
}
332
332
+
i += 1;
333
333
+
}
334
334
+
}
335
335
+
Ok(())
336
336
+
}
268
337
269
269
-
for offset in (start..total_scrobbles).step_by(BATCH_SIZE) {
270
270
-
let scrobbles =
271
271
-
repo::scrobble::get_scrobbles(&pool_clone, offset as i64, BATCH_SIZE as i64)
272
272
-
.await?;
273
273
-
tracing::info!(
274
274
-
offset = %offset.magenta(),
275
275
-
end = %((offset + scrobbles.len() as i64).min(total_scrobbles)).magenta(),
276
276
-
total = %total_scrobbles.magenta(),
277
277
-
"Fetched scrobbles"
278
278
-
);
338
338
+
async fn sync_artist_tracks(pools: &DatabasePools) -> Result<(), Error> {
339
339
+
let total_artist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_tracks")
340
340
+
.fetch_one(&pools.source)
341
341
+
.await?;
342
342
+
let total_artist_tracks = total_artist_tracks.0;
343
343
+
tracing::info!(total = %total_artist_tracks.magenta(), "Total artist tracks to sync");
344
344
+
345
345
+
let start = 0;
346
346
+
let mut i = 1;
347
347
+
348
348
+
for offset in (start..total_artist_tracks).step_by(BATCH_SIZE) {
349
349
+
let artist_tracks =
350
350
+
repo::artist::get_artist_tracks(&pools.source, offset as i64, BATCH_SIZE as i64)
351
351
+
.await?;
352
352
+
tracing::info!(
353
353
+
offset = %offset.magenta(),
354
354
+
end = %((offset + artist_tracks.len() as i64).min(total_artist_tracks)).magenta(),
355
355
+
total = %total_artist_tracks.magenta(),
356
356
+
"Fetched artist tracks"
357
357
+
);
279
358
280
280
-
for scrobble in &scrobbles {
281
281
-
tracing::info!(user_id = %scrobble.user_id.cyan(), track_id = %scrobble.track_id.magenta(), i = %i.magenta(), total = %total_scrobbles.magenta(), "Inserting scrobble");
282
282
-
match repo::scrobble::insert_scrobble(&dest_pool_clone, scrobble).await {
283
283
-
Ok(_) => {}
284
284
-
Err(e) => {
285
285
-
tracing::error!(error = %e, "Failed to insert scrobble");
286
286
-
}
359
359
+
for artist_track in &artist_tracks {
360
360
+
tracing::info!(artist_id = %artist_track.artist_id.cyan(), track_id = %artist_track.track_id.magenta(), i = %i.magenta(), total = %total_artist_tracks.magenta(), "Inserting artist track");
361
361
+
match repo::artist::insert_artist_track(&pools.destination, artist_track).await {
362
362
+
Ok(_) => {}
363
363
+
Err(e) => {
364
364
+
tracing::error!(error = %e, "Failed to insert artist track");
287
365
}
288
288
-
i += 1;
289
366
}
367
367
+
i += 1;
290
368
}
291
291
-
Ok::<(), Error>(())
292
292
-
});
369
369
+
}
370
370
+
Ok(())
371
371
+
}
293
372
294
294
-
let (loved_track_sync, playlist_sync, scrobble_sync) =
295
295
-
tokio::join!(loved_track_sync, playlist_sync, scrobble_sync);
296
296
-
loved_track_sync.context("Loved track sync task failed")??;
297
297
-
playlist_sync.context("Playlist sync task failed")??;
298
298
-
scrobble_sync.context("Scrobble sync task failed")??;
373
373
+
async fn sync_playlist_tracks(pools: &DatabasePools) -> Result<(), Error> {
374
374
+
let total_playlist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlist_tracks")
375
375
+
.fetch_one(&pools.source)
376
376
+
.await?;
377
377
+
let total_playlist_tracks = total_playlist_tracks.0;
378
378
+
tracing::info!(total = %total_playlist_tracks.magenta(), "Total playlist tracks to sync");
299
379
300
300
-
let pool_clone = pool.clone();
301
301
-
let dest_pool_clone = dest_pool.clone();
302
302
-
let album_track_sync = tokio::spawn(async move {
303
303
-
let total_album_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM album_tracks")
304
304
-
.fetch_one(&pool_clone)
305
305
-
.await?;
306
306
-
let total_album_tracks = total_album_tracks.0;
307
307
-
tracing::info!(total = %total_album_tracks.magenta(), "Total album tracks to sync");
380
380
+
let start = 0;
381
381
+
let mut i = 1;
308
382
309
309
-
const BATCH_SIZE: usize = 1000;
383
383
+
for offset in (start..total_playlist_tracks).step_by(BATCH_SIZE) {
384
384
+
let playlist_tracks =
385
385
+
repo::playlist::get_playlist_tracks(&pools.source, offset as i64, BATCH_SIZE as i64)
386
386
+
.await?;
387
387
+
tracing::info!(
388
388
+
offset = %offset.magenta(),
389
389
+
end = %((offset + playlist_tracks.len() as i64).min(total_playlist_tracks)).magenta(),
390
390
+
total = %total_playlist_tracks.magenta(),
391
391
+
"Fetched playlist tracks"
392
392
+
);
310
393
311
311
-
let start = 0;
312
312
-
let mut i = 1;
313
313
-
314
314
-
for offset in (start..total_album_tracks).step_by(BATCH_SIZE) {
315
315
-
let album_tracks =
316
316
-
repo::album::get_album_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64)
317
317
-
.await?;
318
318
-
tracing::info!(
319
319
-
offset = %offset.magenta(),
320
320
-
end = %((offset + album_tracks.len() as i64).min(total_album_tracks)).magenta(),
321
321
-
total = %total_album_tracks.magenta(),
322
322
-
"Fetched album tracks"
323
323
-
);
324
324
-
325
325
-
for album_track in &album_tracks {
326
326
-
tracing::info!(album_id = %album_track.album_id.cyan(), track_id = %album_track.track_id.magenta(), i = %i.magenta(), total = %total_album_tracks.magenta(), "Inserting album track");
327
327
-
match repo::album::insert_album_track(&dest_pool_clone, album_track).await {
328
328
-
Ok(_) => {}
329
329
-
Err(e) => {
330
330
-
tracing::error!(error = %e, "Failed to insert album track");
331
331
-
}
394
394
+
for playlist_track in &playlist_tracks {
395
395
+
tracing::info!(playlist_id = %playlist_track.playlist_id.cyan(), track_id = %playlist_track.track_id.magenta(), i = %i.magenta(), total = %total_playlist_tracks.magenta(), "Inserting playlist track");
396
396
+
match repo::playlist::insert_playlist_track(&pools.destination, playlist_track).await {
397
397
+
Ok(_) => {}
398
398
+
Err(e) => {
399
399
+
tracing::error!(error = %e, "Failed to insert playlist track");
332
400
}
333
333
-
i += 1;
334
401
}
402
402
+
i += 1;
335
403
}
336
336
-
Ok::<(), Error>(())
337
337
-
});
404
404
+
}
405
405
+
Ok(())
406
406
+
}
338
407
339
339
-
let pool_clone = pool.clone();
340
340
-
let dest_pool_clone = dest_pool.clone();
341
341
-
let artist_album_sync = tokio::spawn(async move {
342
342
-
let total_artist_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_albums")
343
343
-
.fetch_one(&pool_clone)
344
344
-
.await?;
345
345
-
let total_artist_albums = total_artist_albums.0;
346
346
-
tracing::info!(total = %total_artist_albums.magenta(), "Total artist albums to sync");
408
408
+
async fn sync_user_albums(pools: &DatabasePools) -> Result<(), Error> {
409
409
+
let total_user_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_albums")
410
410
+
.fetch_one(&pools.source)
411
411
+
.await?;
412
412
+
let total_user_albums = total_user_albums.0;
413
413
+
tracing::info!(total = %total_user_albums.magenta(), "Total user albums to sync");
347
414
348
348
-
const BATCH_SIZE: usize = 1000;
415
415
+
let start = 0;
416
416
+
let mut i = 1;
349
417
350
350
-
let start = 0;
351
351
-
let mut i = 1;
418
418
+
for offset in (start..total_user_albums).step_by(BATCH_SIZE) {
419
419
+
let user_albums =
420
420
+
repo::album::get_user_albums(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
421
421
+
tracing::info!(
422
422
+
offset = %offset.magenta(),
423
423
+
end = %((offset + user_albums.len() as i64).min(total_user_albums)).magenta(),
424
424
+
total = %total_user_albums.magenta(),
425
425
+
"Fetched user albums"
426
426
+
);
352
427
353
353
-
for offset in (start..total_artist_albums).step_by(BATCH_SIZE) {
354
354
-
let artist_albums =
355
355
-
repo::artist::get_artist_albums(&pool_clone, offset as i64, BATCH_SIZE as i64)
356
356
-
.await?;
357
357
-
tracing::info!(
358
358
-
offset = %offset.magenta(),
359
359
-
end = %((offset + artist_albums.len() as i64).min(total_artist_albums)).magenta(),
360
360
-
total = %total_artist_albums.magenta(),
361
361
-
"Fetched artist albums"
362
362
-
);
363
363
-
364
364
-
for artist_album in &artist_albums {
365
365
-
tracing::info!(artist_id = %artist_album.artist_id.cyan(), album_id = %artist_album.album_id.magenta(), i = %i.magenta(), total = %total_artist_albums.magenta(), "Inserting artist album");
366
366
-
match repo::artist::insert_artist_album(&dest_pool_clone, artist_album).await {
367
367
-
Ok(_) => {}
368
368
-
Err(e) => {
369
369
-
tracing::error!(error = %e, "Failed to insert artist album");
370
370
-
}
428
428
+
for user_album in &user_albums {
429
429
+
tracing::info!(user_id = %user_album.user_id.cyan(), album_id = %user_album.album_id.magenta(), i = %i.magenta(), total = %total_user_albums.magenta(), "Inserting user album");
430
430
+
match repo::album::insert_user_album(&pools.destination, user_album).await {
431
431
+
Ok(_) => {}
432
432
+
Err(e) => {
433
433
+
tracing::error!(error = %e, "Failed to insert user album");
371
434
}
372
372
-
i += 1;
373
435
}
436
436
+
i += 1;
374
437
}
375
375
-
Ok::<(), Error>(())
376
376
-
});
438
438
+
}
439
439
+
Ok(())
440
440
+
}
377
441
378
378
-
let pool_clone = pool.clone();
379
379
-
let dest_pool_clone = dest_pool.clone();
380
380
-
let artist_track_sync = tokio::spawn(async move {
381
381
-
let total_artist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_tracks")
382
382
-
.fetch_one(&pool_clone)
383
383
-
.await?;
384
384
-
let total_artist_tracks = total_artist_tracks.0;
385
385
-
tracing::info!(total = %total_artist_tracks.magenta(), "Total artist tracks to sync");
386
386
-
const BATCH_SIZE: usize = 1000;
442
442
+
async fn sync_user_artists(pools: &DatabasePools) -> Result<(), Error> {
443
443
+
let total_user_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_artists")
444
444
+
.fetch_one(&pools.source)
445
445
+
.await?;
446
446
+
let total_user_artists = total_user_artists.0;
447
447
+
tracing::info!(total = %total_user_artists.magenta(), "Total user artists to sync");
387
448
388
388
-
let start = 0;
389
389
-
let mut i = 1;
449
449
+
let start = 0;
450
450
+
let mut i = 1;
390
451
391
391
-
for offset in (start..total_artist_tracks).step_by(BATCH_SIZE) {
392
392
-
let artist_tracks =
393
393
-
repo::artist::get_artist_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64)
394
394
-
.await?;
395
395
-
tracing::info!(
396
396
-
offset = %offset.magenta(),
397
397
-
end = %((offset + artist_tracks.len() as i64).min(total_artist_tracks)).magenta(),
398
398
-
total = %total_artist_tracks.magenta(),
399
399
-
"Fetched artist tracks"
400
400
-
);
452
452
+
for offset in (start..total_user_artists).step_by(BATCH_SIZE) {
453
453
+
let user_artists =
454
454
+
repo::artist::get_user_artists(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
455
455
+
tracing::info!(
456
456
+
offset = %offset.magenta(),
457
457
+
end = %((offset + user_artists.len() as i64).min(total_user_artists)).magenta(),
458
458
+
total = %total_user_artists.magenta(),
459
459
+
"Fetched user artists"
460
460
+
);
401
461
402
402
-
for artist_track in &artist_tracks {
403
403
-
tracing::info!(artist_id = %artist_track.artist_id.cyan(), track_id = %artist_track.track_id.magenta(), i = %i.magenta(), total = %total_artist_tracks.magenta(), "Inserting artist track");
404
404
-
match repo::artist::insert_artist_track(&dest_pool_clone, artist_track).await {
405
405
-
Ok(_) => {}
406
406
-
Err(e) => {
407
407
-
tracing::error!(error = %e, "Failed to insert artist track");
408
408
-
}
462
462
+
for user_artist in &user_artists {
463
463
+
tracing::info!(user_id = %user_artist.user_id.cyan(), artist_id = %user_artist.artist_id.magenta(), i = %i.magenta(), total = %total_user_artists.magenta(), "Inserting user artist");
464
464
+
match repo::artist::insert_user_artist(&pools.destination, user_artist).await {
465
465
+
Ok(_) => {}
466
466
+
Err(e) => {
467
467
+
tracing::error!(error = %e, "Failed to insert user artist");
409
468
}
410
410
-
i += 1;
411
469
}
470
470
+
i += 1;
412
471
}
413
413
-
Ok::<(), Error>(())
414
414
-
});
472
472
+
}
473
473
+
Ok(())
474
474
+
}
415
475
416
416
-
let pool_clone = pool.clone();
417
417
-
let dest_pool_clone = dest_pool.clone();
418
418
-
let playlist_track_sync = tokio::spawn(async move {
419
419
-
let total_playlist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlist_tracks")
420
420
-
.fetch_one(&pool_clone)
421
421
-
.await?;
422
422
-
let total_playlist_tracks = total_playlist_tracks.0;
423
423
-
tracing::info!(total = %total_playlist_tracks.magenta(), "Total playlist tracks to sync");
424
424
-
425
425
-
const BATCH_SIZE: usize = 1000;
476
476
+
async fn sync_user_tracks(pools: &DatabasePools) -> Result<(), Error> {
477
477
+
let total_user_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_tracks")
478
478
+
.fetch_one(&pools.source)
479
479
+
.await?;
480
480
+
let total_user_tracks = total_user_tracks.0;
481
481
+
tracing::info!(total = %total_user_tracks.magenta(), "Total user tracks to sync");
426
482
427
427
-
let start = 0;
428
428
-
let mut i = 1;
483
483
+
let start = 0;
484
484
+
let mut i = 1;
429
485
430
430
-
for offset in (start..total_playlist_tracks).step_by(BATCH_SIZE) {
431
431
-
let playlist_tracks =
432
432
-
repo::playlist::get_playlist_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64)
433
433
-
.await?;
434
434
-
tracing::info!(
435
435
-
offset = %offset.magenta(),
436
436
-
end = %((offset + playlist_tracks.len() as i64).min(total_playlist_tracks)).magenta(),
437
437
-
total = %total_playlist_tracks.magenta(),
438
438
-
"Fetched playlist tracks"
439
439
-
);
486
486
+
for offset in (start..total_user_tracks).step_by(BATCH_SIZE) {
487
487
+
let user_tracks =
488
488
+
repo::track::get_user_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?;
489
489
+
tracing::info!(
490
490
+
offset = %offset.magenta(),
491
491
+
end = %((offset + user_tracks.len() as i64).min(total_user_tracks)).magenta(),
492
492
+
total = %total_user_tracks.magenta(),
493
493
+
"Fetched user tracks"
494
494
+
);
440
495
441
441
-
for playlist_track in &playlist_tracks {
442
442
-
tracing::info!(playlist_id = %playlist_track.playlist_id.cyan(), track_id = %playlist_track.track_id.magenta(), i = %i.magenta(), total = %total_playlist_tracks.magenta(), "Inserting playlist track");
443
443
-
match repo::playlist::insert_playlist_track(&dest_pool_clone, playlist_track).await
444
444
-
{
445
445
-
Ok(_) => {}
446
446
-
Err(e) => {
447
447
-
tracing::error!(error = %e, "Failed to insert playlist track");
448
448
-
}
496
496
+
for user_track in &user_tracks {
497
497
+
tracing::info!(user_id = %user_track.user_id.cyan(), track_id = %user_track.track_id.magenta(), i = %i.magenta(), total = %total_user_tracks.magenta(), "Inserting user track");
498
498
+
match repo::track::insert_user_track(&pools.destination, user_track).await {
499
499
+
Ok(_) => {}
500
500
+
Err(e) => {
501
501
+
tracing::error!(error = %e, "Failed to insert user track");
449
502
}
450
450
-
i += 1;
451
503
}
504
504
+
i += 1;
452
505
}
453
453
-
Ok::<(), Error>(())
454
454
-
});
506
506
+
}
507
507
+
Ok(())
508
508
+
}
455
509
456
456
-
let pool_clone = pool.clone();
457
457
-
let dest_pool_clone = dest_pool.clone();
458
458
-
let user_album_sync = tokio::spawn(async move {
459
459
-
let total_user_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_albums")
460
460
-
.fetch_one(&pool_clone)
461
461
-
.await?;
462
462
-
let total_user_albums = total_user_albums.0;
463
463
-
tracing::info!(total = %total_user_albums.magenta(), "Total user albums to sync");
464
464
-
const BATCH_SIZE: usize = 1000;
510
510
+
async fn sync_user_playlists(pools: &DatabasePools) -> Result<(), Error> {
511
511
+
let total_user_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_playlists")
512
512
+
.fetch_one(&pools.source)
513
513
+
.await?;
514
514
+
let total_user_playlists = total_user_playlists.0;
515
515
+
tracing::info!(total = %total_user_playlists.magenta(), "Total user playlists to sync");
465
516
466
466
-
let start = 0;
467
467
-
let mut i = 1;
517
517
+
let start = 0;
518
518
+
let mut i = 1;
468
519
469
469
-
for offset in (start..total_user_albums).step_by(BATCH_SIZE) {
470
470
-
let user_albums =
471
471
-
repo::album::get_user_albums(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
472
472
-
tracing::info!(
473
473
-
offset = %offset.magenta(),
474
474
-
end = %((offset + user_albums.len() as i64).min(total_user_albums)).magenta(),
475
475
-
total = %total_user_albums.magenta(),
476
476
-
"Fetched user albums"
477
477
-
);
520
520
+
for offset in (start..total_user_playlists).step_by(BATCH_SIZE) {
521
521
+
let user_playlists =
522
522
+
repo::playlist::get_user_playlists(&pools.source, offset as i64, BATCH_SIZE as i64)
523
523
+
.await?;
524
524
+
tracing::info!(
525
525
+
offset = %offset.magenta(),
526
526
+
end = %((offset + user_playlists.len() as i64).min(total_user_playlists)).magenta(),
527
527
+
total = %total_user_playlists.magenta(),
528
528
+
"Fetched user playlists"
529
529
+
);
478
530
479
479
-
for user_album in &user_albums {
480
480
-
tracing::info!(user_id = %user_album.user_id.cyan(), album_id = %user_album.album_id.magenta(), i = %i.magenta(), total = %total_user_albums.magenta(), "Inserting user album");
481
481
-
match repo::album::insert_user_album(&dest_pool_clone, user_album).await {
482
482
-
Ok(_) => {}
483
483
-
Err(e) => {
484
484
-
tracing::error!(error = %e, "Failed to insert user album");
485
485
-
}
531
531
+
for user_playlist in &user_playlists {
532
532
+
tracing::info!(user_id = %user_playlist.user_id.cyan(), playlist_id = %user_playlist.playlist_id.magenta(), i = %i.magenta(), total = %total_user_playlists.magenta(), "Inserting user playlist");
533
533
+
match repo::playlist::insert_user_playlist(&pools.destination, user_playlist).await {
534
534
+
Ok(_) => {}
535
535
+
Err(e) => {
536
536
+
tracing::error!(error = %e, "Failed to insert user playlist");
486
537
}
487
487
-
i += 1;
488
538
}
539
539
+
i += 1;
489
540
}
490
490
-
Ok::<(), Error>(())
541
541
+
}
542
542
+
Ok(())
543
543
+
}
544
544
+
545
545
+
pub async fn pull_data() -> Result<(), Error> {
546
546
+
let pools = setup_database_pools().await?;
547
547
+
548
548
+
// Sync core entities first
549
549
+
let album_sync = tokio::spawn({
550
550
+
let pools = pools.clone();
551
551
+
async move { sync_albums(&pools).await }
491
552
});
492
553
493
493
-
let pool_clone = pool.clone();
494
494
-
let dest_pool_clone = dest_pool.clone();
495
495
-
let user_artist_sync = tokio::spawn(async move {
496
496
-
let total_user_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_artists")
497
497
-
.fetch_one(&pool_clone)
498
498
-
.await?;
499
499
-
let total_user_artists = total_user_artists.0;
500
500
-
tracing::info!(total = %total_user_artists.magenta(), "Total user artists to sync");
501
501
-
const BATCH_SIZE: usize = 1000;
554
554
+
let artist_sync = tokio::spawn({
555
555
+
let pools = pools.clone();
556
556
+
async move { sync_artists(&pools).await }
557
557
+
});
502
558
503
503
-
let start = 0;
504
504
-
let mut i = 1;
559
559
+
let track_sync = tokio::spawn({
560
560
+
let pools = pools.clone();
561
561
+
async move { sync_tracks(&pools).await }
562
562
+
});
563
563
+
564
564
+
let user_sync = tokio::spawn({
565
565
+
let pools = pools.clone();
566
566
+
async move { sync_users(&pools).await }
567
567
+
});
568
568
+
569
569
+
let (album_sync, artist_sync, track_sync, user_sync) =
570
570
+
tokio::join!(album_sync, artist_sync, track_sync, user_sync);
571
571
+
572
572
+
album_sync.context("Album sync task failed")??;
573
573
+
artist_sync.context("Artist sync task failed")??;
574
574
+
track_sync.context("Track sync task failed")??;
575
575
+
user_sync.context("User sync task failed")??;
576
576
+
577
577
+
// Sync relationship entities
578
578
+
let playlist_sync = tokio::spawn({
579
579
+
let pools = pools.clone();
580
580
+
async move { sync_playlists(&pools).await }
581
581
+
});
505
582
506
506
-
for offset in (start..total_user_artists).step_by(BATCH_SIZE) {
507
507
-
let user_artists =
508
508
-
repo::artist::get_user_artists(&pool_clone, offset as i64, BATCH_SIZE as i64)
509
509
-
.await?;
510
510
-
tracing::info!(
511
511
-
offset = %offset.magenta(),
512
512
-
end = %((offset + user_artists.len() as i64).min(total_user_artists)).magenta(),
513
513
-
total = %total_user_artists.magenta(),
514
514
-
"Fetched user artists"
515
515
-
);
583
583
+
let loved_track_sync = tokio::spawn({
584
584
+
let pools = pools.clone();
585
585
+
async move { sync_loved_tracks(&pools).await }
586
586
+
});
516
587
517
517
-
for user_artist in &user_artists {
518
518
-
tracing::info!(user_id = %user_artist.user_id.cyan(), artist_id = %user_artist.artist_id.magenta(), i = %i.magenta(), total = %total_user_artists.magenta(), "Inserting user artist");
519
519
-
match repo::artist::insert_user_artist(&dest_pool_clone, user_artist).await {
520
520
-
Ok(_) => {}
521
521
-
Err(e) => {
522
522
-
tracing::error!(error = %e, "Failed to insert user artist");
523
523
-
}
524
524
-
}
525
525
-
i += 1;
526
526
-
}
527
527
-
}
528
528
-
Ok::<(), Error>(())
588
588
+
let scrobble_sync = tokio::spawn({
589
589
+
let pools = pools.clone();
590
590
+
async move { sync_scrobbles(&pools).await }
529
591
});
530
592
531
531
-
let pool_clone = pool.clone();
532
532
-
let dest_pool_clone = dest_pool.clone();
533
533
-
let user_track_sync = tokio::spawn(async move {
534
534
-
let total_user_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_tracks")
535
535
-
.fetch_one(&pool_clone)
536
536
-
.await?;
537
537
-
let total_user_tracks = total_user_tracks.0;
538
538
-
tracing::info!(total = %total_user_tracks.magenta(), "Total user tracks to sync");
539
539
-
const BATCH_SIZE: usize = 1000;
593
593
+
let (loved_track_sync, playlist_sync, scrobble_sync) =
594
594
+
tokio::join!(loved_track_sync, playlist_sync, scrobble_sync);
595
595
+
loved_track_sync.context("Loved track sync task failed")??;
596
596
+
playlist_sync.context("Playlist sync task failed")??;
597
597
+
scrobble_sync.context("Scrobble sync task failed")??;
540
598
541
541
-
let start = 0;
542
542
-
let mut i = 1;
599
599
+
// Sync junction tables
600
600
+
let album_track_sync = tokio::spawn({
601
601
+
let pools = pools.clone();
602
602
+
async move { sync_album_tracks(&pools).await }
603
603
+
});
543
604
544
544
-
for offset in (start..total_user_tracks).step_by(BATCH_SIZE) {
545
545
-
let user_tracks =
546
546
-
repo::track::get_user_tracks(&pool_clone, offset as i64, BATCH_SIZE as i64).await?;
547
547
-
tracing::info!(
548
548
-
offset = %offset.magenta(),
549
549
-
end = %((offset + user_tracks.len() as i64).min(total_user_tracks)).magenta(),
550
550
-
total = %total_user_tracks.magenta(),
551
551
-
"Fetched user tracks"
552
552
-
);
605
605
+
let artist_album_sync = tokio::spawn({
606
606
+
let pools = pools.clone();
607
607
+
async move { sync_artist_albums(&pools).await }
608
608
+
});
553
609
554
554
-
for user_track in &user_tracks {
555
555
-
tracing::info!(user_id = %user_track.user_id.cyan(), track_id = %user_track.track_id.magenta(), i = %i.magenta(), total = %total_user_tracks.magenta(), "Inserting user track");
556
556
-
match repo::track::insert_user_track(&dest_pool_clone, user_track).await {
557
557
-
Ok(_) => {}
558
558
-
Err(e) => {
559
559
-
tracing::error!(error = %e, "Failed to insert user track");
560
560
-
}
561
561
-
}
562
562
-
i += 1;
563
563
-
}
564
564
-
}
565
565
-
Ok::<(), Error>(())
610
610
+
let artist_track_sync = tokio::spawn({
611
611
+
let pools = pools.clone();
612
612
+
async move { sync_artist_tracks(&pools).await }
566
613
});
567
614
568
568
-
let pool_clone = pool.clone();
569
569
-
let dest_pool_clone = dest_pool.clone();
570
570
-
let user_playlist_sync = tokio::spawn(async move {
571
571
-
let total_user_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_playlists")
572
572
-
.fetch_one(&pool_clone)
573
573
-
.await?;
615
615
+
let playlist_track_sync = tokio::spawn({
616
616
+
let pools = pools.clone();
617
617
+
async move { sync_playlist_tracks(&pools).await }
618
618
+
});
574
619
575
575
-
let total_user_playlists = total_user_playlists.0;
576
576
-
tracing::info!(total = %total_user_playlists.magenta(), "Total user playlists to sync");
577
577
-
const BATCH_SIZE: usize = 1000;
620
620
+
let user_album_sync = tokio::spawn({
621
621
+
let pools = pools.clone();
622
622
+
async move { sync_user_albums(&pools).await }
623
623
+
});
578
624
579
579
-
let start = 0;
580
580
-
let mut i = 1;
625
625
+
let user_artist_sync = tokio::spawn({
626
626
+
let pools = pools.clone();
627
627
+
async move { sync_user_artists(&pools).await }
628
628
+
});
581
629
582
582
-
for offset in (start..total_user_playlists).step_by(BATCH_SIZE) {
583
583
-
let user_playlists =
584
584
-
repo::playlist::get_user_playlists(&pool_clone, offset as i64, BATCH_SIZE as i64)
585
585
-
.await?;
586
586
-
tracing::info!(
587
587
-
offset = %offset.magenta(),
588
588
-
end = %((offset + user_playlists.len() as i64).min(total_user_playlists)).magenta(),
589
589
-
total = %total_user_playlists.magenta(),
590
590
-
"Fetched user playlists"
591
591
-
);
630
630
+
let user_track_sync = tokio::spawn({
631
631
+
let pools = pools.clone();
632
632
+
async move { sync_user_tracks(&pools).await }
633
633
+
});
592
634
593
593
-
for user_playlist in &user_playlists {
594
594
-
tracing::info!(user_id = %user_playlist.user_id.cyan(), playlist_id = %user_playlist.playlist_id.magenta(), i = %i.magenta(), total = %total_user_playlists.magenta(), "Inserting user playlist");
595
595
-
match repo::playlist::insert_user_playlist(&dest_pool_clone, user_playlist).await {
596
596
-
Ok(_) => {}
597
597
-
Err(e) => {
598
598
-
tracing::error!(error = %e, "Failed to insert user playlist");
599
599
-
}
600
600
-
}
601
601
-
i += 1;
602
602
-
}
603
603
-
}
604
604
-
Ok::<(), Error>(())
635
635
+
let user_playlist_sync = tokio::spawn({
636
636
+
let pools = pools.clone();
637
637
+
async move { sync_user_playlists(&pools).await }
605
638
});
606
639
607
640
let (