import 'package:drift/drift.dart'; import 'app_database.dart'; import '../models/download_models.dart'; /// Extension methods on AppDatabase for download operations extension DownloadDatabaseOperations on AppDatabase { /// Insert a new download into the database Future insertDownload({ required String serverId, required String ratingKey, required String globalKey, required String type, String? parentRatingKey, String? grandparentRatingKey, required int status, }) async { await into(downloadedMedia).insert( DownloadedMediaCompanion.insert( serverId: serverId, ratingKey: ratingKey, globalKey: globalKey, type: type, parentRatingKey: Value(parentRatingKey), grandparentRatingKey: Value(grandparentRatingKey), status: status, ), mode: InsertMode.insertOrReplace, ); } /// Add item to download queue Future addToQueue({ required String mediaGlobalKey, int priority = 0, bool downloadSubtitles = true, bool downloadArtwork = true, }) async { await into(downloadQueue).insert( DownloadQueueCompanion.insert( mediaGlobalKey: mediaGlobalKey, priority: Value(priority), addedAt: DateTime.now().millisecondsSinceEpoch, downloadSubtitles: Value(downloadSubtitles), downloadArtwork: Value(downloadArtwork), ), mode: InsertMode.insertOrReplace, ); } /// Get next item from queue (highest priority, oldest first) /// Only returns items that are not paused Future getNextQueueItem() async { // Join with downloadedMedia to check status and filter out paused items final query = select( downloadQueue, ).join([innerJoin(downloadedMedia, downloadedMedia.globalKey.equalsExp(downloadQueue.mediaGlobalKey))]); query ..where(downloadedMedia.status.equals(DownloadStatus.queued.index)) ..orderBy([ OrderingTerm(expression: downloadQueue.priority, mode: OrderingMode.desc), OrderingTerm(expression: downloadQueue.addedAt), ]) ..limit(1); final result = await query.getSingleOrNull(); return result?.readTable(downloadQueue); } /// Update download status Future updateDownloadStatus(String globalKey, int status) async { await (update( downloadedMedia, )..where((t) => t.globalKey.equals(globalKey))).write(DownloadedMediaCompanion(status: Value(status))); } /// Update download progress Future updateDownloadProgress(String globalKey, int progress, int downloadedBytes, int totalBytes) async { await (update(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).write( DownloadedMediaCompanion( progress: Value(progress), downloadedBytes: Value(downloadedBytes), totalBytes: Value(totalBytes), ), ); } /// Update video file path Future updateVideoFilePath(String globalKey, String filePath) async { await (update(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).write( DownloadedMediaCompanion( videoFilePath: Value(filePath), downloadedAt: Value(DateTime.now().millisecondsSinceEpoch), ), ); } /// Update artwork paths Future updateArtworkPaths({required String globalKey, String? thumbPath}) async { await (update( downloadedMedia, )..where((t) => t.globalKey.equals(globalKey))).write(DownloadedMediaCompanion(thumbPath: Value(thumbPath))); } /// Update download error and increment retry count Future updateDownloadError(String globalKey, String errorMessage) async { // Get current retry count to increment it final existing = await getDownloadedMedia(globalKey); final currentCount = existing?.retryCount ?? 0; await (update(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).write( DownloadedMediaCompanion(errorMessage: Value(errorMessage), retryCount: Value(currentCount + 1)), ); } /// Clear download error and reset retry count (for retry) Future clearDownloadError(String globalKey) async { await (update(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).write( const DownloadedMediaCompanion(errorMessage: Value(null), retryCount: Value(0)), ); } /// Remove item from queue Future removeFromQueue(String mediaGlobalKey) async { await (delete(downloadQueue)..where((t) => t.mediaGlobalKey.equals(mediaGlobalKey))).go(); } /// Get downloaded media item Future getDownloadedMedia(String globalKey) { return (select(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).getSingleOrNull(); } /// Delete a download Future deleteDownload(String globalKey) async { await (delete(downloadedMedia)..where((t) => t.globalKey.equals(globalKey))).go(); await (delete(downloadQueue)..where((t) => t.mediaGlobalKey.equals(globalKey))).go(); } /// Get all downloaded episodes for a season Future> getEpisodesBySeason(String seasonKey) { return (select(downloadedMedia)..where((t) => t.parentRatingKey.equals(seasonKey))).get(); } /// Get all downloaded episodes for a show Future> getEpisodesByShow(String showKey) { return (select(downloadedMedia)..where((t) => t.grandparentRatingKey.equals(showKey))).get(); } /// Get all downloaded items for a specific server Future> getDownloadsByServerId(String serverId) { return (select(downloadedMedia)..where((t) => t.serverId.equals(serverId))).get(); } /// Update the background_downloader task ID for a download Future updateBgTaskId(String globalKey, String? taskId) async { await (update( downloadedMedia, )..where((t) => t.globalKey.equals(globalKey))).write(DownloadedMediaCompanion(bgTaskId: Value(taskId))); } /// Get the background_downloader task ID for a download Future getBgTaskId(String globalKey) async { final item = await getDownloadedMedia(globalKey); return item?.bgTaskId; } }