worldhopper/lib/repositories/downloaded_series_repository.dart
Felipe M. d2497f8aed
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
feat: add background download service with error handling improvements
Add Android foreground service support for downloads to prevent OS
process killing during long operations. Fix multiple error handling
issues: foreground service leak on exceptions, polling stuck forever
on failure, double-counting chapters on retry, silent retry failures
without user feedback, and app crash if background service init fails.
Replace inaccurate failed chapter count heuristic with actual data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 11:44:37 +01:00

117 lines
3.5 KiB
Dart

import 'package:sqflite/sqflite.dart';
import 'package:worldhopper/database/database.dart';
import 'package:worldhopper/database/tables/downloaded_series_table.dart';
import 'package:worldhopper/models/downloaded_series.dart';
/// Repository for downloaded series operations
class DownloadedSeriesRepository {
final DatabaseHelper _dbHelper;
DownloadedSeriesRepository(this._dbHelper);
/// Get all downloaded series
Future<List<DownloadedSeries>> getAll() async {
final db = await _dbHelper.database;
final results = await db.query(
DownloadedSeriesTable.tableName,
orderBy: 'updated_at DESC',
);
return results.map((row) => DownloadedSeries.fromDatabase(row)).toList();
}
/// Get downloaded series by server ID and feed URL
Future<DownloadedSeries?> getByFeedUrl(
String serverId, String feedUrl) async {
final db = await _dbHelper.database;
final results = await db.query(
DownloadedSeriesTable.tableName,
where: 'server_id = ? AND series_feed_url = ?',
whereArgs: [serverId, feedUrl],
);
if (results.isEmpty) return null;
return DownloadedSeries.fromDatabase(results.first);
}
/// Get downloaded series by ID
Future<DownloadedSeries?> getById(String id) async {
final db = await _dbHelper.database;
final results = await db.query(
DownloadedSeriesTable.tableName,
where: 'id = ?',
whereArgs: [id],
);
if (results.isEmpty) return null;
return DownloadedSeries.fromDatabase(results.first);
}
/// Save or update a downloaded series
Future<void> save(DownloadedSeries series) async {
final db = await _dbHelper.database;
await db.insert(
DownloadedSeriesTable.tableName,
series.toDatabase(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
/// Update the status of a downloaded series
Future<void> updateStatus(String id, DownloadStatus status) async {
final db = await _dbHelper.database;
await db.update(
DownloadedSeriesTable.tableName,
{
'status': status.name,
'updated_at': DateTime.now().millisecondsSinceEpoch,
},
where: 'id = ?',
whereArgs: [id],
);
}
/// Update the downloaded chapters count
Future<void> updateDownloadedCount(String id, int count) async {
final db = await _dbHelper.database;
final updates = <String, dynamic>{
'downloaded_chapters_count': count,
'updated_at': DateTime.now().millisecondsSinceEpoch,
};
await db.update(
DownloadedSeriesTable.tableName,
updates,
where: 'id = ?',
whereArgs: [id],
);
}
/// Delete a downloaded series by ID
Future<void> delete(String id) async {
final db = await _dbHelper.database;
await db.delete(
DownloadedSeriesTable.tableName,
where: 'id = ?',
whereArgs: [id],
);
}
/// Get downloaded series by status
Future<List<DownloadedSeries>> getByStatus(DownloadStatus status) async {
final db = await _dbHelper.database;
final results = await db.query(
DownloadedSeriesTable.tableName,
where: 'status = ?',
whereArgs: [status.name],
orderBy: 'updated_at DESC',
);
return results.map((row) => DownloadedSeries.fromDatabase(row)).toList();
}
/// Delete all downloaded series for a server
Future<void> deleteByServer(String serverId) async {
final db = await _dbHelper.database;
await db.delete(
DownloadedSeriesTable.tableName,
where: 'server_id = ?',
whereArgs: [serverId],
);
}
}