Skip to content

Commit ce8ca12

Browse files
committed
more problems
1 parent b7245e3 commit ce8ca12

1 file changed

Lines changed: 130 additions & 27 deletions

File tree

lib/services/supabase_sync_service.dart

Lines changed: 130 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,60 @@ class SupabaseSyncService {
3535
try {
3636
final localHistory = await _localDb.getAllWatchHistory();
3737

38+
// Get ALL remote data with pagination
39+
final allRemoteData = <Map<String, dynamic>>[];
40+
int offset = 0;
41+
const batchSize = 1000;
3842

39-
final remoteResponse = await _client!
40-
.from(_tableName)
41-
.select('tmdb_id,type,season_number,episode_number');
42-
43-
final List<dynamic> remoteData = remoteResponse as List<dynamic>;
43+
while (true) {
44+
final remoteResponse = await _client!
45+
.from(_tableName)
46+
.select('tmdb_id,type,season_number,episode_number')
47+
.range(offset, offset + batchSize - 1);
48+
49+
final List<dynamic> batch = remoteResponse as List<dynamic>;
50+
if (batch.isEmpty) break;
51+
52+
allRemoteData.addAll(batch.cast<Map<String, dynamic>>());
53+
54+
if (batch.length < batchSize) break; // Last batch
55+
offset += batchSize;
56+
}
4457

58+
debugPrint('Retrieved ${allRemoteData.length} remote items for comparison');
4559

60+
// Create a set of local items for efficient lookup
4661
final localItemKeys = localHistory.map((item) {
4762
return '${item.tmdbId}_${item.type}_${item.seasonNumber ?? 'null'}_${item.episodeNumber ?? 'null'}';
4863
}).toSet();
4964

50-
51-
final itemsToDelete = remoteData.where((remoteItem) {
65+
// Find remote items that don't exist locally (i.e., were deleted locally)
66+
final itemsToDelete = allRemoteData.where((remoteItem) {
5267
final key = '${remoteItem['tmdb_id']}_${remoteItem['type']}_${remoteItem['season_number'] ?? 'null'}_${remoteItem['episode_number'] ?? 'null'}';
5368
return !localItemKeys.contains(key);
5469
}).toList();
5570

56-
71+
// Delete items from Supabase that were removed locally (in batches)
72+
int deletedCount = 0;
5773
for (final itemToDelete in itemsToDelete) {
5874
await _client.from(_tableName).delete().match({
5975
'tmdb_id': itemToDelete['tmdb_id'],
6076
'type': itemToDelete['type'],
6177
'season_number': itemToDelete['season_number'],
6278
'episode_number': itemToDelete['episode_number'],
6379
});
80+
deletedCount++;
6481
}
6582

66-
debugPrint('Deleted ${itemsToDelete.length} items from Supabase that were removed locally');
83+
debugPrint('Deleted $deletedCount items from Supabase that were removed locally');
6784

85+
// Upload/update all local items in batches
86+
int uploadedCount = 0;
87+
const uploadBatchSize = 100; // Smaller batches for uploads to avoid timeouts
6888

69-
for (final item in localHistory) {
70-
final data = {
89+
for (int i = 0; i < localHistory.length; i += uploadBatchSize) {
90+
final batch = localHistory.skip(i).take(uploadBatchSize).toList();
91+
final batchData = batch.map((item) => {
7192
'tmdb_id': item.tmdbId,
7293
'title': item.title,
7394
'type': item.type,
@@ -78,15 +99,18 @@ class SupabaseSyncService {
7899
'episode_title': item.episodeTitle,
79100
'user_rating': item.userRating,
80101
'notes': item.notes,
81-
};
102+
}).toList();
82103

83104
await _client.from(_tableName).upsert(
84-
data,
105+
batchData,
85106
onConflict: 'tmdb_id,type,season_number,episode_number',
86107
);
108+
109+
uploadedCount += batch.length;
110+
debugPrint('Uploaded batch: $uploadedCount/${localHistory.length} items');
87111
}
88112

89-
debugPrint('Successfully uploaded ${localHistory.length} watch history items to Supabase');
113+
debugPrint('Successfully uploaded $uploadedCount watch history items to Supabase');
90114
return true;
91115
} catch (e) {
92116
debugPrint('Error uploading watch history to Supabase: $e');
@@ -99,14 +123,44 @@ class SupabaseSyncService {
99123
if (!isConfigured) return false;
100124

101125
try {
102-
final response = await _client!
103-
.from(_tableName)
104-
.select()
105-
.order('watched_at', ascending: false);
126+
// Get ALL remote data with pagination
127+
final allRemoteData = <Map<String, dynamic>>[];
128+
int offset = 0;
129+
const batchSize = 1000;
130+
131+
while (true) {
132+
final response = await _client!
133+
.from(_tableName)
134+
.select()
135+
.order('watched_at', ascending: false)
136+
.range(offset, offset + batchSize - 1);
106137

107-
final List<dynamic> data = response as List<dynamic>;
138+
final List<dynamic> batch = response as List<dynamic>;
139+
if (batch.isEmpty) break;
140+
141+
allRemoteData.addAll(batch.cast<Map<String, dynamic>>());
142+
143+
if (batch.length < batchSize) break; // Last batch
144+
offset += batchSize;
145+
146+
debugPrint('Downloaded batch: ${allRemoteData.length} items so far...');
147+
}
148+
149+
debugPrint('Retrieved ${allRemoteData.length} total items from Supabase');
150+
151+
// Get existing local items to avoid duplicates
152+
final existingLocalItems = await _localDb.getAllWatchHistory();
153+
final existingKeys = <String>{};
154+
155+
for (final item in existingLocalItems) {
156+
final key = '${item.tmdbId}_${item.type}_${item.seasonNumber ?? 'null'}_${item.episodeNumber ?? 'null'}';
157+
existingKeys.add(key);
158+
}
159+
160+
int newItemsCount = 0;
161+
int updatedItemsCount = 0;
108162

109-
for (final item in data) {
163+
for (final item in allRemoteData) {
110164
final watchHistoryItem = WatchHistoryItem(
111165
tmdbId: item['tmdb_id'],
112166
title: item['title'],
@@ -120,17 +174,53 @@ class SupabaseSyncService {
120174
notes: item['notes'],
121175
);
122176

177+
final key = '${watchHistoryItem.tmdbId}_${watchHistoryItem.type}_${watchHistoryItem.seasonNumber ?? 'null'}_${watchHistoryItem.episodeNumber ?? 'null'}';
123178

124-
await _localDb.insertWatchHistoryItem(watchHistoryItem);
179+
if (existingKeys.contains(key)) {
180+
// Item already exists, check if we need to update it
181+
final existingItem = await _getExistingLocalItem(watchHistoryItem);
182+
if (existingItem != null && _shouldUpdateItem(existingItem, watchHistoryItem)) {
183+
await _localDb.updateWatchHistoryItem(watchHistoryItem);
184+
updatedItemsCount++;
185+
}
186+
} else {
187+
// New item, insert it
188+
await _localDb.insertWatchHistoryItem(watchHistoryItem);
189+
newItemsCount++;
190+
}
125191
}
126192

127-
debugPrint('Successfully downloaded ${data.length} watch history items from Supabase');
193+
debugPrint('Successfully downloaded $newItemsCount new items and updated $updatedItemsCount items from Supabase');
128194
return true;
129195
} catch (e) {
130196
debugPrint('Error downloading watch history from Supabase: $e');
131197
return false;
132198
}
133199
}
200+
201+
// Helper method to get existing local item
202+
Future<WatchHistoryItem?> _getExistingLocalItem(WatchHistoryItem item) async {
203+
final existingItems = await _localDb.getWatchHistoryByTmdbId(item.tmdbId, item.type);
204+
205+
for (final existing in existingItems) {
206+
if ((existing.seasonNumber == item.seasonNumber || (existing.seasonNumber == null && item.seasonNumber == null)) &&
207+
(existing.episodeNumber == item.episodeNumber || (existing.episodeNumber == null && item.episodeNumber == null))) {
208+
return existing;
209+
}
210+
}
211+
return null;
212+
}
213+
214+
// Helper method to determine if an item should be updated
215+
bool _shouldUpdateItem(WatchHistoryItem existing, WatchHistoryItem remote) {
216+
// Update if remote item is newer or has different data
217+
return remote.watchedAt.isAfter(existing.watchedAt) ||
218+
existing.title != remote.title ||
219+
existing.posterPath != remote.posterPath ||
220+
existing.episodeTitle != remote.episodeTitle ||
221+
existing.userRating != remote.userRating ||
222+
existing.notes != remote.notes;
223+
}
134224

135225

136226
Future<bool> syncWatchHistory() async {
@@ -239,11 +329,24 @@ class SupabaseSyncService {
239329
int remoteCount = 0;
240330
if (isConfigured) {
241331
try {
242-
final response = await _client!
243-
.from(_tableName)
244-
.select('id')
245-
.count(CountOption.exact);
246-
remoteCount = response.count;
332+
// Count items by fetching all IDs in batches (most reliable method)
333+
int offset = 0;
334+
const batchSize = 1000;
335+
336+
while (true) {
337+
final response = await _client!
338+
.from(_tableName)
339+
.select('id')
340+
.range(offset, offset + batchSize - 1);
341+
342+
final List<dynamic> batch = response as List<dynamic>;
343+
if (batch.isEmpty) break;
344+
345+
remoteCount += batch.length;
346+
347+
if (batch.length < batchSize) break;
348+
offset += batchSize;
349+
}
247350
} catch (e) {
248351
debugPrint('Error getting remote count: $e');
249352
}

0 commit comments

Comments
 (0)