From ee020d81bd7bbf5d00a1aa8a0db612273cd34ae6 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:57:03 +0200 Subject: [PATCH] refactor: trakt/download hardening and cleanup sweep --- lib/database/app_database.g.dart | 1985 +++++------------ lib/focus/focusable_wrapper.dart | 4 +- lib/i18n/da.i18n.json | 4 + lib/i18n/de.i18n.json | 4 + lib/i18n/en.i18n.json | 4 + lib/i18n/es.i18n.json | 4 + lib/i18n/fr.i18n.json | 4 + lib/i18n/it.i18n.json | 4 + lib/i18n/ja.i18n.json | 4 + lib/i18n/ko.i18n.json | 4 + lib/i18n/nb.i18n.json | 4 + lib/i18n/nl.i18n.json | 4 + lib/i18n/pl.i18n.json | 4 + lib/i18n/pt.i18n.json | 4 + lib/i18n/ru.i18n.json | 4 + lib/i18n/strings.g.dart | 4 +- lib/i18n/strings_da.g.dart | 12 +- lib/i18n/strings_de.g.dart | 12 +- lib/i18n/strings_en.g.dart | 20 +- lib/i18n/strings_es.g.dart | 12 +- lib/i18n/strings_fr.g.dart | 12 +- lib/i18n/strings_it.g.dart | 12 +- lib/i18n/strings_ja.g.dart | 12 +- lib/i18n/strings_ko.g.dart | 12 +- lib/i18n/strings_nb.g.dart | 12 +- lib/i18n/strings_nl.g.dart | 12 +- lib/i18n/strings_pl.g.dart | 12 +- lib/i18n/strings_pt.g.dart | 12 +- lib/i18n/strings_ru.g.dart | 12 +- lib/i18n/strings_sv.g.dart | 12 +- lib/i18n/strings_zh.g.dart | 12 +- lib/i18n/sv.i18n.json | 4 + lib/i18n/zh.i18n.json | 4 + lib/main.dart | 4 + lib/mixins/tab_navigation_mixin.dart | 2 +- .../companion_remote/remote_session.g.dart | 86 +- lib/models/play_queue_response.g.dart | 30 +- lib/models/plex_library.g.dart | 25 +- lib/models/plex_metadata.g.dart | 115 +- lib/models/plex_playlist.g.dart | 39 +- lib/mpv/models.dart | 6 +- lib/mpv/player/player_base.dart | 5 - lib/providers/download_provider.dart | 185 +- lib/screens/auth_screen.dart | 2 +- lib/screens/discover_screen.dart | 6 +- .../libraries/adaptive_media_grid.dart | 7 +- .../libraries/tabs/library_browse_tab.dart | 2 +- lib/screens/livetv/tabs/guide_tab.dart | 2 +- lib/screens/media_detail_screen.dart | 4 +- .../profile/profile_switch_screen.dart | 6 +- lib/screens/profile/user_avatar_widget.dart | 18 +- .../settings/appearance_settings_screen.dart | 249 +-- lib/screens/video_player_screen.dart | 2 +- lib/services/download_manager_service.dart | 52 +- lib/services/download_storage_service.dart | 24 +- lib/services/sync_rule_executor.dart | 56 +- lib/services/trakt/trakt_sync_queue.dart | 57 +- lib/services/trakt/trakt_sync_service.dart | 65 +- lib/utils/content_utils.dart | 5 - lib/utils/episode_collection.dart | 52 + lib/utils/plex_cache_parser.dart | 25 - lib/utils/plex_image_helper.dart | 11 - lib/widgets/download_status_icon.dart | 136 ++ lib/widgets/download_tree_view.dart | 37 +- lib/widgets/episode_card.dart | 131 +- lib/widgets/media_context_menu.dart | 6 +- 66 files changed, 1526 insertions(+), 2167 deletions(-) create mode 100644 lib/utils/episode_collection.dart create mode 100644 lib/widgets/download_status_icon.dart diff --git a/lib/database/app_database.g.dart b/lib/database/app_database.g.dart index 1a3aa882b..555aca000 100644 --- a/lib/database/app_database.g.dart +++ b/lib/database/app_database.g.dart @@ -3,8 +3,7 @@ part of 'app_database.dart'; // ignore_for_file: type=lint -class $DownloadedMediaTable extends DownloadedMedia - with TableInfo<$DownloadedMediaTable, DownloadedMediaItem> { +class $DownloadedMediaTable extends DownloadedMedia with TableInfo<$DownloadedMediaTable, DownloadedMediaItem> { @override final GeneratedDatabase attachedDatabase; final String? _alias; @@ -18,13 +17,9 @@ class $DownloadedMediaTable extends DownloadedMedia hasAutoIncrement: true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'PRIMARY KEY AUTOINCREMENT', - ), - ); - static const VerificationMeta _serverIdMeta = const VerificationMeta( - 'serverId', + defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'), ); + static const VerificationMeta _serverIdMeta = const VerificationMeta('serverId'); @override late final GeneratedColumn serverId = GeneratedColumn( 'server_id', @@ -33,9 +28,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _ratingKeyMeta = const VerificationMeta( - 'ratingKey', - ); + static const VerificationMeta _ratingKeyMeta = const VerificationMeta('ratingKey'); @override late final GeneratedColumn ratingKey = GeneratedColumn( 'rating_key', @@ -44,9 +37,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _globalKeyMeta = const VerificationMeta( - 'globalKey', - ); + static const VerificationMeta _globalKeyMeta = const VerificationMeta('globalKey'); @override late final GeneratedColumn globalKey = GeneratedColumn( 'global_key', @@ -65,9 +56,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _parentRatingKeyMeta = const VerificationMeta( - 'parentRatingKey', - ); + static const VerificationMeta _parentRatingKeyMeta = const VerificationMeta('parentRatingKey'); @override late final GeneratedColumn parentRatingKey = GeneratedColumn( 'parent_rating_key', @@ -76,17 +65,15 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: false, ); - static const VerificationMeta _grandparentRatingKeyMeta = - const VerificationMeta('grandparentRatingKey'); + static const VerificationMeta _grandparentRatingKeyMeta = const VerificationMeta('grandparentRatingKey'); @override - late final GeneratedColumn grandparentRatingKey = - GeneratedColumn( - 'grandparent_rating_key', - aliasedName, - true, - type: DriftSqlType.string, - requiredDuringInsert: false, - ); + late final GeneratedColumn grandparentRatingKey = GeneratedColumn( + 'grandparent_rating_key', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); static const VerificationMeta _statusMeta = const VerificationMeta('status'); @override late final GeneratedColumn status = GeneratedColumn( @@ -96,9 +83,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _progressMeta = const VerificationMeta( - 'progress', - ); + static const VerificationMeta _progressMeta = const VerificationMeta('progress'); @override late final GeneratedColumn progress = GeneratedColumn( 'progress', @@ -108,9 +93,7 @@ class $DownloadedMediaTable extends DownloadedMedia requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _totalBytesMeta = const VerificationMeta( - 'totalBytes', - ); + static const VerificationMeta _totalBytesMeta = const VerificationMeta('totalBytes'); @override late final GeneratedColumn totalBytes = GeneratedColumn( 'total_bytes', @@ -119,9 +102,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.int, requiredDuringInsert: false, ); - static const VerificationMeta _downloadedBytesMeta = const VerificationMeta( - 'downloadedBytes', - ); + static const VerificationMeta _downloadedBytesMeta = const VerificationMeta('downloadedBytes'); @override late final GeneratedColumn downloadedBytes = GeneratedColumn( 'downloaded_bytes', @@ -131,9 +112,7 @@ class $DownloadedMediaTable extends DownloadedMedia requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _videoFilePathMeta = const VerificationMeta( - 'videoFilePath', - ); + static const VerificationMeta _videoFilePathMeta = const VerificationMeta('videoFilePath'); @override late final GeneratedColumn videoFilePath = GeneratedColumn( 'video_file_path', @@ -142,9 +121,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: false, ); - static const VerificationMeta _thumbPathMeta = const VerificationMeta( - 'thumbPath', - ); + static const VerificationMeta _thumbPathMeta = const VerificationMeta('thumbPath'); @override late final GeneratedColumn thumbPath = GeneratedColumn( 'thumb_path', @@ -153,9 +130,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: false, ); - static const VerificationMeta _downloadedAtMeta = const VerificationMeta( - 'downloadedAt', - ); + static const VerificationMeta _downloadedAtMeta = const VerificationMeta('downloadedAt'); @override late final GeneratedColumn downloadedAt = GeneratedColumn( 'downloaded_at', @@ -164,9 +139,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.int, requiredDuringInsert: false, ); - static const VerificationMeta _errorMessageMeta = const VerificationMeta( - 'errorMessage', - ); + static const VerificationMeta _errorMessageMeta = const VerificationMeta('errorMessage'); @override late final GeneratedColumn errorMessage = GeneratedColumn( 'error_message', @@ -175,9 +148,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: false, ); - static const VerificationMeta _retryCountMeta = const VerificationMeta( - 'retryCount', - ); + static const VerificationMeta _retryCountMeta = const VerificationMeta('retryCount'); @override late final GeneratedColumn retryCount = GeneratedColumn( 'retry_count', @@ -187,9 +158,7 @@ class $DownloadedMediaTable extends DownloadedMedia requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _bgTaskIdMeta = const VerificationMeta( - 'bgTaskId', - ); + static const VerificationMeta _bgTaskIdMeta = const VerificationMeta('bgTaskId'); @override late final GeneratedColumn bgTaskId = GeneratedColumn( 'bg_task_id', @@ -198,9 +167,7 @@ class $DownloadedMediaTable extends DownloadedMedia type: DriftSqlType.string, requiredDuringInsert: false, ); - static const VerificationMeta _mediaIndexMeta = const VerificationMeta( - 'mediaIndex', - ); + static const VerificationMeta _mediaIndexMeta = const VerificationMeta('mediaIndex'); @override late final GeneratedColumn mediaIndex = GeneratedColumn( 'media_index', @@ -237,144 +204,84 @@ class $DownloadedMediaTable extends DownloadedMedia String get actualTableName => $name; static const String $name = 'downloaded_media'; @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { + VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } if (data.containsKey('server_id')) { - context.handle( - _serverIdMeta, - serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta), - ); + context.handle(_serverIdMeta, serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta)); } else if (isInserting) { context.missing(_serverIdMeta); } if (data.containsKey('rating_key')) { - context.handle( - _ratingKeyMeta, - ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta), - ); + context.handle(_ratingKeyMeta, ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta)); } else if (isInserting) { context.missing(_ratingKeyMeta); } if (data.containsKey('global_key')) { - context.handle( - _globalKeyMeta, - globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta), - ); + context.handle(_globalKeyMeta, globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta)); } else if (isInserting) { context.missing(_globalKeyMeta); } if (data.containsKey('type')) { - context.handle( - _typeMeta, - type.isAcceptableOrUnknown(data['type']!, _typeMeta), - ); + context.handle(_typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); } else if (isInserting) { context.missing(_typeMeta); } if (data.containsKey('parent_rating_key')) { context.handle( _parentRatingKeyMeta, - parentRatingKey.isAcceptableOrUnknown( - data['parent_rating_key']!, - _parentRatingKeyMeta, - ), + parentRatingKey.isAcceptableOrUnknown(data['parent_rating_key']!, _parentRatingKeyMeta), ); } if (data.containsKey('grandparent_rating_key')) { context.handle( _grandparentRatingKeyMeta, - grandparentRatingKey.isAcceptableOrUnknown( - data['grandparent_rating_key']!, - _grandparentRatingKeyMeta, - ), + grandparentRatingKey.isAcceptableOrUnknown(data['grandparent_rating_key']!, _grandparentRatingKeyMeta), ); } if (data.containsKey('status')) { - context.handle( - _statusMeta, - status.isAcceptableOrUnknown(data['status']!, _statusMeta), - ); + context.handle(_statusMeta, status.isAcceptableOrUnknown(data['status']!, _statusMeta)); } else if (isInserting) { context.missing(_statusMeta); } if (data.containsKey('progress')) { - context.handle( - _progressMeta, - progress.isAcceptableOrUnknown(data['progress']!, _progressMeta), - ); + context.handle(_progressMeta, progress.isAcceptableOrUnknown(data['progress']!, _progressMeta)); } if (data.containsKey('total_bytes')) { - context.handle( - _totalBytesMeta, - totalBytes.isAcceptableOrUnknown(data['total_bytes']!, _totalBytesMeta), - ); + context.handle(_totalBytesMeta, totalBytes.isAcceptableOrUnknown(data['total_bytes']!, _totalBytesMeta)); } if (data.containsKey('downloaded_bytes')) { context.handle( _downloadedBytesMeta, - downloadedBytes.isAcceptableOrUnknown( - data['downloaded_bytes']!, - _downloadedBytesMeta, - ), + downloadedBytes.isAcceptableOrUnknown(data['downloaded_bytes']!, _downloadedBytesMeta), ); } if (data.containsKey('video_file_path')) { context.handle( _videoFilePathMeta, - videoFilePath.isAcceptableOrUnknown( - data['video_file_path']!, - _videoFilePathMeta, - ), + videoFilePath.isAcceptableOrUnknown(data['video_file_path']!, _videoFilePathMeta), ); } if (data.containsKey('thumb_path')) { - context.handle( - _thumbPathMeta, - thumbPath.isAcceptableOrUnknown(data['thumb_path']!, _thumbPathMeta), - ); + context.handle(_thumbPathMeta, thumbPath.isAcceptableOrUnknown(data['thumb_path']!, _thumbPathMeta)); } if (data.containsKey('downloaded_at')) { - context.handle( - _downloadedAtMeta, - downloadedAt.isAcceptableOrUnknown( - data['downloaded_at']!, - _downloadedAtMeta, - ), - ); + context.handle(_downloadedAtMeta, downloadedAt.isAcceptableOrUnknown(data['downloaded_at']!, _downloadedAtMeta)); } if (data.containsKey('error_message')) { - context.handle( - _errorMessageMeta, - errorMessage.isAcceptableOrUnknown( - data['error_message']!, - _errorMessageMeta, - ), - ); + context.handle(_errorMessageMeta, errorMessage.isAcceptableOrUnknown(data['error_message']!, _errorMessageMeta)); } if (data.containsKey('retry_count')) { - context.handle( - _retryCountMeta, - retryCount.isAcceptableOrUnknown(data['retry_count']!, _retryCountMeta), - ); + context.handle(_retryCountMeta, retryCount.isAcceptableOrUnknown(data['retry_count']!, _retryCountMeta)); } if (data.containsKey('bg_task_id')) { - context.handle( - _bgTaskIdMeta, - bgTaskId.isAcceptableOrUnknown(data['bg_task_id']!, _bgTaskIdMeta), - ); + context.handle(_bgTaskIdMeta, bgTaskId.isAcceptableOrUnknown(data['bg_task_id']!, _bgTaskIdMeta)); } if (data.containsKey('media_index')) { - context.handle( - _mediaIndexMeta, - mediaIndex.isAcceptableOrUnknown(data['media_index']!, _mediaIndexMeta), - ); + context.handle(_mediaIndexMeta, mediaIndex.isAcceptableOrUnknown(data['media_index']!, _mediaIndexMeta)); } return context; } @@ -385,26 +292,11 @@ class $DownloadedMediaTable extends DownloadedMedia DownloadedMediaItem map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return DownloadedMediaItem( - id: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}id'], - )!, - serverId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}server_id'], - )!, - ratingKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}rating_key'], - )!, - globalKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}global_key'], - )!, - type: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}type'], - )!, + id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + serverId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}server_id'])!, + ratingKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}rating_key'])!, + globalKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}global_key'])!, + type: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}type'])!, parentRatingKey: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}parent_rating_key'], @@ -413,50 +305,17 @@ class $DownloadedMediaTable extends DownloadedMedia DriftSqlType.string, data['${effectivePrefix}grandparent_rating_key'], ), - status: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}status'], - )!, - progress: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}progress'], - )!, - totalBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}total_bytes'], - ), - downloadedBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}downloaded_bytes'], - )!, - videoFilePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}video_file_path'], - ), - thumbPath: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}thumb_path'], - ), - downloadedAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}downloaded_at'], - ), - errorMessage: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}error_message'], - ), - retryCount: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}retry_count'], - )!, - bgTaskId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}bg_task_id'], - ), - mediaIndex: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}media_index'], - )!, + status: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}status'])!, + progress: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}progress'])!, + totalBytes: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}total_bytes']), + downloadedBytes: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}downloaded_bytes'])!, + videoFilePath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}video_file_path']), + thumbPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_path']), + downloadedAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}downloaded_at']), + errorMessage: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}error_message']), + retryCount: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}retry_count'])!, + bgTaskId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}bg_task_id']), + mediaIndex: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}media_index'])!, ); } @@ -466,8 +325,7 @@ class $DownloadedMediaTable extends DownloadedMedia } } -class DownloadedMediaItem extends DataClass - implements Insertable { +class DownloadedMediaItem extends DataClass implements Insertable { final int id; final String serverId; final String ratingKey; @@ -553,42 +411,25 @@ class DownloadedMediaItem extends DataClass ratingKey: Value(ratingKey), globalKey: Value(globalKey), type: Value(type), - parentRatingKey: parentRatingKey == null && nullToAbsent - ? const Value.absent() - : Value(parentRatingKey), + parentRatingKey: parentRatingKey == null && nullToAbsent ? const Value.absent() : Value(parentRatingKey), grandparentRatingKey: grandparentRatingKey == null && nullToAbsent ? const Value.absent() : Value(grandparentRatingKey), status: Value(status), progress: Value(progress), - totalBytes: totalBytes == null && nullToAbsent - ? const Value.absent() - : Value(totalBytes), + totalBytes: totalBytes == null && nullToAbsent ? const Value.absent() : Value(totalBytes), downloadedBytes: Value(downloadedBytes), - videoFilePath: videoFilePath == null && nullToAbsent - ? const Value.absent() - : Value(videoFilePath), - thumbPath: thumbPath == null && nullToAbsent - ? const Value.absent() - : Value(thumbPath), - downloadedAt: downloadedAt == null && nullToAbsent - ? const Value.absent() - : Value(downloadedAt), - errorMessage: errorMessage == null && nullToAbsent - ? const Value.absent() - : Value(errorMessage), + videoFilePath: videoFilePath == null && nullToAbsent ? const Value.absent() : Value(videoFilePath), + thumbPath: thumbPath == null && nullToAbsent ? const Value.absent() : Value(thumbPath), + downloadedAt: downloadedAt == null && nullToAbsent ? const Value.absent() : Value(downloadedAt), + errorMessage: errorMessage == null && nullToAbsent ? const Value.absent() : Value(errorMessage), retryCount: Value(retryCount), - bgTaskId: bgTaskId == null && nullToAbsent - ? const Value.absent() - : Value(bgTaskId), + bgTaskId: bgTaskId == null && nullToAbsent ? const Value.absent() : Value(bgTaskId), mediaIndex: Value(mediaIndex), ); } - factory DownloadedMediaItem.fromJson( - Map json, { - ValueSerializer? serializer, - }) { + factory DownloadedMediaItem.fromJson(Map json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return DownloadedMediaItem( id: serializer.fromJson(json['id']), @@ -597,9 +438,7 @@ class DownloadedMediaItem extends DataClass globalKey: serializer.fromJson(json['globalKey']), type: serializer.fromJson(json['type']), parentRatingKey: serializer.fromJson(json['parentRatingKey']), - grandparentRatingKey: serializer.fromJson( - json['grandparentRatingKey'], - ), + grandparentRatingKey: serializer.fromJson(json['grandparentRatingKey']), status: serializer.fromJson(json['status']), progress: serializer.fromJson(json['progress']), totalBytes: serializer.fromJson(json['totalBytes']), @@ -663,19 +502,13 @@ class DownloadedMediaItem extends DataClass ratingKey: ratingKey ?? this.ratingKey, globalKey: globalKey ?? this.globalKey, type: type ?? this.type, - parentRatingKey: parentRatingKey.present - ? parentRatingKey.value - : this.parentRatingKey, - grandparentRatingKey: grandparentRatingKey.present - ? grandparentRatingKey.value - : this.grandparentRatingKey, + parentRatingKey: parentRatingKey.present ? parentRatingKey.value : this.parentRatingKey, + grandparentRatingKey: grandparentRatingKey.present ? grandparentRatingKey.value : this.grandparentRatingKey, status: status ?? this.status, progress: progress ?? this.progress, totalBytes: totalBytes.present ? totalBytes.value : this.totalBytes, downloadedBytes: downloadedBytes ?? this.downloadedBytes, - videoFilePath: videoFilePath.present - ? videoFilePath.value - : this.videoFilePath, + videoFilePath: videoFilePath.present ? videoFilePath.value : this.videoFilePath, thumbPath: thumbPath.present ? thumbPath.value : this.thumbPath, downloadedAt: downloadedAt.present ? downloadedAt.value : this.downloadedAt, errorMessage: errorMessage.present ? errorMessage.value : this.errorMessage, @@ -690,37 +523,21 @@ class DownloadedMediaItem extends DataClass ratingKey: data.ratingKey.present ? data.ratingKey.value : this.ratingKey, globalKey: data.globalKey.present ? data.globalKey.value : this.globalKey, type: data.type.present ? data.type.value : this.type, - parentRatingKey: data.parentRatingKey.present - ? data.parentRatingKey.value - : this.parentRatingKey, + parentRatingKey: data.parentRatingKey.present ? data.parentRatingKey.value : this.parentRatingKey, grandparentRatingKey: data.grandparentRatingKey.present ? data.grandparentRatingKey.value : this.grandparentRatingKey, status: data.status.present ? data.status.value : this.status, progress: data.progress.present ? data.progress.value : this.progress, - totalBytes: data.totalBytes.present - ? data.totalBytes.value - : this.totalBytes, - downloadedBytes: data.downloadedBytes.present - ? data.downloadedBytes.value - : this.downloadedBytes, - videoFilePath: data.videoFilePath.present - ? data.videoFilePath.value - : this.videoFilePath, + totalBytes: data.totalBytes.present ? data.totalBytes.value : this.totalBytes, + downloadedBytes: data.downloadedBytes.present ? data.downloadedBytes.value : this.downloadedBytes, + videoFilePath: data.videoFilePath.present ? data.videoFilePath.value : this.videoFilePath, thumbPath: data.thumbPath.present ? data.thumbPath.value : this.thumbPath, - downloadedAt: data.downloadedAt.present - ? data.downloadedAt.value - : this.downloadedAt, - errorMessage: data.errorMessage.present - ? data.errorMessage.value - : this.errorMessage, - retryCount: data.retryCount.present - ? data.retryCount.value - : this.retryCount, + downloadedAt: data.downloadedAt.present ? data.downloadedAt.value : this.downloadedAt, + errorMessage: data.errorMessage.present ? data.errorMessage.value : this.errorMessage, + retryCount: data.retryCount.present ? data.retryCount.value : this.retryCount, bgTaskId: data.bgTaskId.present ? data.bgTaskId.value : this.bgTaskId, - mediaIndex: data.mediaIndex.present - ? data.mediaIndex.value - : this.mediaIndex, + mediaIndex: data.mediaIndex.present ? data.mediaIndex.value : this.mediaIndex, ); } @@ -884,8 +701,7 @@ class DownloadedMediaCompanion extends UpdateCompanion { if (globalKey != null) 'global_key': globalKey, if (type != null) 'type': type, if (parentRatingKey != null) 'parent_rating_key': parentRatingKey, - if (grandparentRatingKey != null) - 'grandparent_rating_key': grandparentRatingKey, + if (grandparentRatingKey != null) 'grandparent_rating_key': grandparentRatingKey, if (status != null) 'status': status, if (progress != null) 'progress': progress, if (totalBytes != null) 'total_bytes': totalBytes, @@ -964,9 +780,7 @@ class DownloadedMediaCompanion extends UpdateCompanion { map['parent_rating_key'] = Variable(parentRatingKey.value); } if (grandparentRatingKey.present) { - map['grandparent_rating_key'] = Variable( - grandparentRatingKey.value, - ); + map['grandparent_rating_key'] = Variable(grandparentRatingKey.value); } if (status.present) { map['status'] = Variable(status.value); @@ -1030,8 +844,7 @@ class DownloadedMediaCompanion extends UpdateCompanion { } } -class $DownloadQueueTable extends DownloadQueue - with TableInfo<$DownloadQueueTable, DownloadQueueItem> { +class $DownloadQueueTable extends DownloadQueue with TableInfo<$DownloadQueueTable, DownloadQueueItem> { @override final GeneratedDatabase attachedDatabase; final String? _alias; @@ -1045,13 +858,9 @@ class $DownloadQueueTable extends DownloadQueue hasAutoIncrement: true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'PRIMARY KEY AUTOINCREMENT', - ), - ); - static const VerificationMeta _mediaGlobalKeyMeta = const VerificationMeta( - 'mediaGlobalKey', + defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'), ); + static const VerificationMeta _mediaGlobalKeyMeta = const VerificationMeta('mediaGlobalKey'); @override late final GeneratedColumn mediaGlobalKey = GeneratedColumn( 'media_global_key', @@ -1061,9 +870,7 @@ class $DownloadQueueTable extends DownloadQueue requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'), ); - static const VerificationMeta _priorityMeta = const VerificationMeta( - 'priority', - ); + static const VerificationMeta _priorityMeta = const VerificationMeta('priority'); @override late final GeneratedColumn priority = GeneratedColumn( 'priority', @@ -1073,9 +880,7 @@ class $DownloadQueueTable extends DownloadQueue requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _addedAtMeta = const VerificationMeta( - 'addedAt', - ); + static const VerificationMeta _addedAtMeta = const VerificationMeta('addedAt'); @override late final GeneratedColumn addedAt = GeneratedColumn( 'added_at', @@ -1084,9 +889,7 @@ class $DownloadQueueTable extends DownloadQueue type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _downloadSubtitlesMeta = const VerificationMeta( - 'downloadSubtitles', - ); + static const VerificationMeta _downloadSubtitlesMeta = const VerificationMeta('downloadSubtitles'); @override late final GeneratedColumn downloadSubtitles = GeneratedColumn( 'download_subtitles', @@ -1094,14 +897,10 @@ class $DownloadQueueTable extends DownloadQueue false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("download_subtitles" IN (0, 1))', - ), + defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("download_subtitles" IN (0, 1))'), defaultValue: const Constant(true), ); - static const VerificationMeta _downloadArtworkMeta = const VerificationMeta( - 'downloadArtwork', - ); + static const VerificationMeta _downloadArtworkMeta = const VerificationMeta('downloadArtwork'); @override late final GeneratedColumn downloadArtwork = GeneratedColumn( 'download_artwork', @@ -1109,30 +908,18 @@ class $DownloadQueueTable extends DownloadQueue false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("download_artwork" IN (0, 1))', - ), + defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("download_artwork" IN (0, 1))'), defaultValue: const Constant(true), ); @override - List get $columns => [ - id, - mediaGlobalKey, - priority, - addedAt, - downloadSubtitles, - downloadArtwork, - ]; + List get $columns => [id, mediaGlobalKey, priority, addedAt, downloadSubtitles, downloadArtwork]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'download_queue'; @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { + VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -1141,44 +928,29 @@ class $DownloadQueueTable extends DownloadQueue if (data.containsKey('media_global_key')) { context.handle( _mediaGlobalKeyMeta, - mediaGlobalKey.isAcceptableOrUnknown( - data['media_global_key']!, - _mediaGlobalKeyMeta, - ), + mediaGlobalKey.isAcceptableOrUnknown(data['media_global_key']!, _mediaGlobalKeyMeta), ); } else if (isInserting) { context.missing(_mediaGlobalKeyMeta); } if (data.containsKey('priority')) { - context.handle( - _priorityMeta, - priority.isAcceptableOrUnknown(data['priority']!, _priorityMeta), - ); + context.handle(_priorityMeta, priority.isAcceptableOrUnknown(data['priority']!, _priorityMeta)); } if (data.containsKey('added_at')) { - context.handle( - _addedAtMeta, - addedAt.isAcceptableOrUnknown(data['added_at']!, _addedAtMeta), - ); + context.handle(_addedAtMeta, addedAt.isAcceptableOrUnknown(data['added_at']!, _addedAtMeta)); } else if (isInserting) { context.missing(_addedAtMeta); } if (data.containsKey('download_subtitles')) { context.handle( _downloadSubtitlesMeta, - downloadSubtitles.isAcceptableOrUnknown( - data['download_subtitles']!, - _downloadSubtitlesMeta, - ), + downloadSubtitles.isAcceptableOrUnknown(data['download_subtitles']!, _downloadSubtitlesMeta), ); } if (data.containsKey('download_artwork')) { context.handle( _downloadArtworkMeta, - downloadArtwork.isAcceptableOrUnknown( - data['download_artwork']!, - _downloadArtworkMeta, - ), + downloadArtwork.isAcceptableOrUnknown(data['download_artwork']!, _downloadArtworkMeta), ); } return context; @@ -1190,22 +962,13 @@ class $DownloadQueueTable extends DownloadQueue DownloadQueueItem map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return DownloadQueueItem( - id: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}id'], - )!, + id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, mediaGlobalKey: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}media_global_key'], )!, - priority: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}priority'], - )!, - addedAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}added_at'], - )!, + priority: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}priority'])!, + addedAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}added_at'])!, downloadSubtitles: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}download_subtitles'], @@ -1223,8 +986,7 @@ class $DownloadQueueTable extends DownloadQueue } } -class DownloadQueueItem extends DataClass - implements Insertable { +class DownloadQueueItem extends DataClass implements Insertable { final int id; final String mediaGlobalKey; final int priority; @@ -1262,10 +1024,7 @@ class DownloadQueueItem extends DataClass ); } - factory DownloadQueueItem.fromJson( - Map json, { - ValueSerializer? serializer, - }) { + factory DownloadQueueItem.fromJson(Map json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return DownloadQueueItem( id: serializer.fromJson(json['id']), @@ -1307,17 +1066,11 @@ class DownloadQueueItem extends DataClass DownloadQueueItem copyWithCompanion(DownloadQueueCompanion data) { return DownloadQueueItem( id: data.id.present ? data.id.value : this.id, - mediaGlobalKey: data.mediaGlobalKey.present - ? data.mediaGlobalKey.value - : this.mediaGlobalKey, + mediaGlobalKey: data.mediaGlobalKey.present ? data.mediaGlobalKey.value : this.mediaGlobalKey, priority: data.priority.present ? data.priority.value : this.priority, addedAt: data.addedAt.present ? data.addedAt.value : this.addedAt, - downloadSubtitles: data.downloadSubtitles.present - ? data.downloadSubtitles.value - : this.downloadSubtitles, - downloadArtwork: data.downloadArtwork.present - ? data.downloadArtwork.value - : this.downloadArtwork, + downloadSubtitles: data.downloadSubtitles.present ? data.downloadSubtitles.value : this.downloadSubtitles, + downloadArtwork: data.downloadArtwork.present ? data.downloadArtwork.value : this.downloadArtwork, ); } @@ -1335,14 +1088,7 @@ class DownloadQueueItem extends DataClass } @override - int get hashCode => Object.hash( - id, - mediaGlobalKey, - priority, - addedAt, - downloadSubtitles, - downloadArtwork, - ); + int get hashCode => Object.hash(id, mediaGlobalKey, priority, addedAt, downloadSubtitles, downloadArtwork); @override bool operator ==(Object other) => identical(this, other) || @@ -1453,15 +1199,12 @@ class DownloadQueueCompanion extends UpdateCompanion { } } -class $ApiCacheTable extends ApiCache - with TableInfo<$ApiCacheTable, ApiCacheData> { +class $ApiCacheTable extends ApiCache with TableInfo<$ApiCacheTable, ApiCacheData> { @override final GeneratedDatabase attachedDatabase; final String? _alias; $ApiCacheTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _cacheKeyMeta = const VerificationMeta( - 'cacheKey', - ); + static const VerificationMeta _cacheKeyMeta = const VerificationMeta('cacheKey'); @override late final GeneratedColumn cacheKey = GeneratedColumn( 'cache_key', @@ -1487,14 +1230,10 @@ class $ApiCacheTable extends ApiCache false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("pinned" IN (0, 1))', - ), + defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), defaultValue: const Constant(false), ); - static const VerificationMeta _cachedAtMeta = const VerificationMeta( - 'cachedAt', - ); + static const VerificationMeta _cachedAtMeta = const VerificationMeta('cachedAt'); @override late final GeneratedColumn cachedAt = GeneratedColumn( 'cached_at', @@ -1512,39 +1251,24 @@ class $ApiCacheTable extends ApiCache String get actualTableName => $name; static const String $name = 'api_cache'; @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { + VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('cache_key')) { - context.handle( - _cacheKeyMeta, - cacheKey.isAcceptableOrUnknown(data['cache_key']!, _cacheKeyMeta), - ); + context.handle(_cacheKeyMeta, cacheKey.isAcceptableOrUnknown(data['cache_key']!, _cacheKeyMeta)); } else if (isInserting) { context.missing(_cacheKeyMeta); } if (data.containsKey('data')) { - context.handle( - _dataMeta, - this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), - ); + context.handle(_dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('pinned')) { - context.handle( - _pinnedMeta, - pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta), - ); + context.handle(_pinnedMeta, pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta)); } if (data.containsKey('cached_at')) { - context.handle( - _cachedAtMeta, - cachedAt.isAcceptableOrUnknown(data['cached_at']!, _cachedAtMeta), - ); + context.handle(_cachedAtMeta, cachedAt.isAcceptableOrUnknown(data['cached_at']!, _cachedAtMeta)); } return context; } @@ -1555,22 +1279,10 @@ class $ApiCacheTable extends ApiCache ApiCacheData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ApiCacheData( - cacheKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}cache_key'], - )!, - data: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}data'], - )!, - pinned: attachedDatabase.typeMapping.read( - DriftSqlType.bool, - data['${effectivePrefix}pinned'], - )!, - cachedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, - data['${effectivePrefix}cached_at'], - )!, + cacheKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}cache_key'])!, + data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + pinned: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!, + cachedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}cached_at'])!, ); } @@ -1592,12 +1304,7 @@ class ApiCacheData extends DataClass implements Insertable { /// Timestamp for cache invalidation (optional future use) final DateTime cachedAt; - const ApiCacheData({ - required this.cacheKey, - required this.data, - required this.pinned, - required this.cachedAt, - }); + const ApiCacheData({required this.cacheKey, required this.data, required this.pinned, required this.cachedAt}); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1617,10 +1324,7 @@ class ApiCacheData extends DataClass implements Insertable { ); } - factory ApiCacheData.fromJson( - Map json, { - ValueSerializer? serializer, - }) { + factory ApiCacheData.fromJson(Map json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return ApiCacheData( cacheKey: serializer.fromJson(json['cacheKey']), @@ -1640,12 +1344,7 @@ class ApiCacheData extends DataClass implements Insertable { }; } - ApiCacheData copyWith({ - String? cacheKey, - String? data, - bool? pinned, - DateTime? cachedAt, - }) => ApiCacheData( + ApiCacheData copyWith({String? cacheKey, String? data, bool? pinned, DateTime? cachedAt}) => ApiCacheData( cacheKey: cacheKey ?? this.cacheKey, data: data ?? this.data, pinned: pinned ?? this.pinned, @@ -1785,13 +1484,9 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress hasAutoIncrement: true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'PRIMARY KEY AUTOINCREMENT', - ), - ); - static const VerificationMeta _serverIdMeta = const VerificationMeta( - 'serverId', + defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'), ); + static const VerificationMeta _serverIdMeta = const VerificationMeta('serverId'); @override late final GeneratedColumn serverId = GeneratedColumn( 'server_id', @@ -1800,9 +1495,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _ratingKeyMeta = const VerificationMeta( - 'ratingKey', - ); + static const VerificationMeta _ratingKeyMeta = const VerificationMeta('ratingKey'); @override late final GeneratedColumn ratingKey = GeneratedColumn( 'rating_key', @@ -1811,9 +1504,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _globalKeyMeta = const VerificationMeta( - 'globalKey', - ); + static const VerificationMeta _globalKeyMeta = const VerificationMeta('globalKey'); @override late final GeneratedColumn globalKey = GeneratedColumn( 'global_key', @@ -1822,9 +1513,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _actionTypeMeta = const VerificationMeta( - 'actionType', - ); + static const VerificationMeta _actionTypeMeta = const VerificationMeta('actionType'); @override late final GeneratedColumn actionType = GeneratedColumn( 'action_type', @@ -1833,9 +1522,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _viewOffsetMeta = const VerificationMeta( - 'viewOffset', - ); + static const VerificationMeta _viewOffsetMeta = const VerificationMeta('viewOffset'); @override late final GeneratedColumn viewOffset = GeneratedColumn( 'view_offset', @@ -1844,9 +1531,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.int, requiredDuringInsert: false, ); - static const VerificationMeta _durationMeta = const VerificationMeta( - 'duration', - ); + static const VerificationMeta _durationMeta = const VerificationMeta('duration'); @override late final GeneratedColumn duration = GeneratedColumn( 'duration', @@ -1855,9 +1540,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.int, requiredDuringInsert: false, ); - static const VerificationMeta _shouldMarkWatchedMeta = const VerificationMeta( - 'shouldMarkWatched', - ); + static const VerificationMeta _shouldMarkWatchedMeta = const VerificationMeta('shouldMarkWatched'); @override late final GeneratedColumn shouldMarkWatched = GeneratedColumn( 'should_mark_watched', @@ -1865,14 +1548,10 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("should_mark_watched" IN (0, 1))', - ), + defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("should_mark_watched" IN (0, 1))'), defaultValue: const Constant(false), ); - static const VerificationMeta _createdAtMeta = const VerificationMeta( - 'createdAt', - ); + static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); @override late final GeneratedColumn createdAt = GeneratedColumn( 'created_at', @@ -1881,9 +1560,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _updatedAtMeta = const VerificationMeta( - 'updatedAt', - ); + static const VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); @override late final GeneratedColumn updatedAt = GeneratedColumn( 'updated_at', @@ -1892,9 +1569,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _syncAttemptsMeta = const VerificationMeta( - 'syncAttempts', - ); + static const VerificationMeta _syncAttemptsMeta = const VerificationMeta('syncAttempts'); @override late final GeneratedColumn syncAttempts = GeneratedColumn( 'sync_attempts', @@ -1904,9 +1579,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _lastErrorMeta = const VerificationMeta( - 'lastError', - ); + static const VerificationMeta _lastErrorMeta = const VerificationMeta('lastError'); @override late final GeneratedColumn lastError = GeneratedColumn( 'last_error', @@ -1936,98 +1609,59 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress String get actualTableName => $name; static const String $name = 'offline_watch_progress'; @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { + VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } if (data.containsKey('server_id')) { - context.handle( - _serverIdMeta, - serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta), - ); + context.handle(_serverIdMeta, serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta)); } else if (isInserting) { context.missing(_serverIdMeta); } if (data.containsKey('rating_key')) { - context.handle( - _ratingKeyMeta, - ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta), - ); + context.handle(_ratingKeyMeta, ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta)); } else if (isInserting) { context.missing(_ratingKeyMeta); } if (data.containsKey('global_key')) { - context.handle( - _globalKeyMeta, - globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta), - ); + context.handle(_globalKeyMeta, globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta)); } else if (isInserting) { context.missing(_globalKeyMeta); } if (data.containsKey('action_type')) { - context.handle( - _actionTypeMeta, - actionType.isAcceptableOrUnknown(data['action_type']!, _actionTypeMeta), - ); + context.handle(_actionTypeMeta, actionType.isAcceptableOrUnknown(data['action_type']!, _actionTypeMeta)); } else if (isInserting) { context.missing(_actionTypeMeta); } if (data.containsKey('view_offset')) { - context.handle( - _viewOffsetMeta, - viewOffset.isAcceptableOrUnknown(data['view_offset']!, _viewOffsetMeta), - ); + context.handle(_viewOffsetMeta, viewOffset.isAcceptableOrUnknown(data['view_offset']!, _viewOffsetMeta)); } if (data.containsKey('duration')) { - context.handle( - _durationMeta, - duration.isAcceptableOrUnknown(data['duration']!, _durationMeta), - ); + context.handle(_durationMeta, duration.isAcceptableOrUnknown(data['duration']!, _durationMeta)); } if (data.containsKey('should_mark_watched')) { context.handle( _shouldMarkWatchedMeta, - shouldMarkWatched.isAcceptableOrUnknown( - data['should_mark_watched']!, - _shouldMarkWatchedMeta, - ), + shouldMarkWatched.isAcceptableOrUnknown(data['should_mark_watched']!, _shouldMarkWatchedMeta), ); } if (data.containsKey('created_at')) { - context.handle( - _createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), - ); + context.handle(_createdAtMeta, createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } else if (isInserting) { context.missing(_createdAtMeta); } if (data.containsKey('updated_at')) { - context.handle( - _updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), - ); + context.handle(_updatedAtMeta, updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } else if (isInserting) { context.missing(_updatedAtMeta); } if (data.containsKey('sync_attempts')) { - context.handle( - _syncAttemptsMeta, - syncAttempts.isAcceptableOrUnknown( - data['sync_attempts']!, - _syncAttemptsMeta, - ), - ); + context.handle(_syncAttemptsMeta, syncAttempts.isAcceptableOrUnknown(data['sync_attempts']!, _syncAttemptsMeta)); } if (data.containsKey('last_error')) { - context.handle( - _lastErrorMeta, - lastError.isAcceptableOrUnknown(data['last_error']!, _lastErrorMeta), - ); + context.handle(_lastErrorMeta, lastError.isAcceptableOrUnknown(data['last_error']!, _lastErrorMeta)); } return context; } @@ -2035,60 +1669,24 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress @override Set get $primaryKey => {id}; @override - OfflineWatchProgressItem map( - Map data, { - String? tablePrefix, - }) { + OfflineWatchProgressItem map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return OfflineWatchProgressItem( - id: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}id'], - )!, - serverId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}server_id'], - )!, - ratingKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}rating_key'], - )!, - globalKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}global_key'], - )!, - actionType: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}action_type'], - )!, - viewOffset: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}view_offset'], - ), - duration: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}duration'], - ), + id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + serverId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}server_id'])!, + ratingKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}rating_key'])!, + globalKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}global_key'])!, + actionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}action_type'])!, + viewOffset: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}view_offset']), + duration: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration']), shouldMarkWatched: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}should_mark_watched'], )!, - createdAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}created_at'], - )!, - updatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}updated_at'], - )!, - syncAttempts: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}sync_attempts'], - )!, - lastError: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}last_error'], - ), + createdAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}updated_at'])!, + syncAttempts: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}sync_attempts'])!, + lastError: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}last_error']), ); } @@ -2098,8 +1696,7 @@ class $OfflineWatchProgressTable extends OfflineWatchProgress } } -class OfflineWatchProgressItem extends DataClass - implements Insertable { +class OfflineWatchProgressItem extends DataClass implements Insertable { /// Auto-incrementing primary key final int id; @@ -2181,26 +1778,17 @@ class OfflineWatchProgressItem extends DataClass ratingKey: Value(ratingKey), globalKey: Value(globalKey), actionType: Value(actionType), - viewOffset: viewOffset == null && nullToAbsent - ? const Value.absent() - : Value(viewOffset), - duration: duration == null && nullToAbsent - ? const Value.absent() - : Value(duration), + viewOffset: viewOffset == null && nullToAbsent ? const Value.absent() : Value(viewOffset), + duration: duration == null && nullToAbsent ? const Value.absent() : Value(duration), shouldMarkWatched: Value(shouldMarkWatched), createdAt: Value(createdAt), updatedAt: Value(updatedAt), syncAttempts: Value(syncAttempts), - lastError: lastError == null && nullToAbsent - ? const Value.absent() - : Value(lastError), + lastError: lastError == null && nullToAbsent ? const Value.absent() : Value(lastError), ); } - factory OfflineWatchProgressItem.fromJson( - Map json, { - ValueSerializer? serializer, - }) { + factory OfflineWatchProgressItem.fromJson(Map json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return OfflineWatchProgressItem( id: serializer.fromJson(json['id']), @@ -2263,29 +1851,19 @@ class OfflineWatchProgressItem extends DataClass syncAttempts: syncAttempts ?? this.syncAttempts, lastError: lastError.present ? lastError.value : this.lastError, ); - OfflineWatchProgressItem copyWithCompanion( - OfflineWatchProgressCompanion data, - ) { + OfflineWatchProgressItem copyWithCompanion(OfflineWatchProgressCompanion data) { return OfflineWatchProgressItem( id: data.id.present ? data.id.value : this.id, serverId: data.serverId.present ? data.serverId.value : this.serverId, ratingKey: data.ratingKey.present ? data.ratingKey.value : this.ratingKey, globalKey: data.globalKey.present ? data.globalKey.value : this.globalKey, - actionType: data.actionType.present - ? data.actionType.value - : this.actionType, - viewOffset: data.viewOffset.present - ? data.viewOffset.value - : this.viewOffset, + actionType: data.actionType.present ? data.actionType.value : this.actionType, + viewOffset: data.viewOffset.present ? data.viewOffset.value : this.viewOffset, duration: data.duration.present ? data.duration.value : this.duration, - shouldMarkWatched: data.shouldMarkWatched.present - ? data.shouldMarkWatched.value - : this.shouldMarkWatched, + shouldMarkWatched: data.shouldMarkWatched.present ? data.shouldMarkWatched.value : this.shouldMarkWatched, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - syncAttempts: data.syncAttempts.present - ? data.syncAttempts.value - : this.syncAttempts, + syncAttempts: data.syncAttempts.present ? data.syncAttempts.value : this.syncAttempts, lastError: data.lastError.present ? data.lastError.value : this.lastError, ); } @@ -2342,8 +1920,7 @@ class OfflineWatchProgressItem extends DataClass other.lastError == this.lastError); } -class OfflineWatchProgressCompanion - extends UpdateCompanion { +class OfflineWatchProgressCompanion extends UpdateCompanion { final Value id; final Value serverId; final Value ratingKey; @@ -2511,8 +2088,7 @@ class OfflineWatchProgressCompanion } } -class $SyncRulesTable extends SyncRules - with TableInfo<$SyncRulesTable, SyncRuleItem> { +class $SyncRulesTable extends SyncRules with TableInfo<$SyncRulesTable, SyncRuleItem> { @override final GeneratedDatabase attachedDatabase; final String? _alias; @@ -2526,13 +2102,9 @@ class $SyncRulesTable extends SyncRules hasAutoIncrement: true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'PRIMARY KEY AUTOINCREMENT', - ), - ); - static const VerificationMeta _serverIdMeta = const VerificationMeta( - 'serverId', + defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'), ); + static const VerificationMeta _serverIdMeta = const VerificationMeta('serverId'); @override late final GeneratedColumn serverId = GeneratedColumn( 'server_id', @@ -2541,9 +2113,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _ratingKeyMeta = const VerificationMeta( - 'ratingKey', - ); + static const VerificationMeta _ratingKeyMeta = const VerificationMeta('ratingKey'); @override late final GeneratedColumn ratingKey = GeneratedColumn( 'rating_key', @@ -2552,9 +2122,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _globalKeyMeta = const VerificationMeta( - 'globalKey', - ); + static const VerificationMeta _globalKeyMeta = const VerificationMeta('globalKey'); @override late final GeneratedColumn globalKey = GeneratedColumn( 'global_key', @@ -2564,9 +2132,7 @@ class $SyncRulesTable extends SyncRules requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'), ); - static const VerificationMeta _targetTypeMeta = const VerificationMeta( - 'targetType', - ); + static const VerificationMeta _targetTypeMeta = const VerificationMeta('targetType'); @override late final GeneratedColumn targetType = GeneratedColumn( 'target_type', @@ -2575,9 +2141,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _episodeCountMeta = const VerificationMeta( - 'episodeCount', - ); + static const VerificationMeta _episodeCountMeta = const VerificationMeta('episodeCount'); @override late final GeneratedColumn episodeCount = GeneratedColumn( 'episode_count', @@ -2586,9 +2150,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _enabledMeta = const VerificationMeta( - 'enabled', - ); + static const VerificationMeta _enabledMeta = const VerificationMeta('enabled'); @override late final GeneratedColumn enabled = GeneratedColumn( 'enabled', @@ -2596,14 +2158,10 @@ class $SyncRulesTable extends SyncRules false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("enabled" IN (0, 1))', - ), + defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("enabled" IN (0, 1))'), defaultValue: const Constant(true), ); - static const VerificationMeta _createdAtMeta = const VerificationMeta( - 'createdAt', - ); + static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); @override late final GeneratedColumn createdAt = GeneratedColumn( 'created_at', @@ -2612,9 +2170,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.int, requiredDuringInsert: true, ); - static const VerificationMeta _lastExecutedAtMeta = const VerificationMeta( - 'lastExecutedAt', - ); + static const VerificationMeta _lastExecutedAtMeta = const VerificationMeta('lastExecutedAt'); @override late final GeneratedColumn lastExecutedAt = GeneratedColumn( 'last_executed_at', @@ -2623,9 +2179,7 @@ class $SyncRulesTable extends SyncRules type: DriftSqlType.int, requiredDuringInsert: false, ); - static const VerificationMeta _mediaIndexMeta = const VerificationMeta( - 'mediaIndex', - ); + static const VerificationMeta _mediaIndexMeta = const VerificationMeta('mediaIndex'); @override late final GeneratedColumn mediaIndex = GeneratedColumn( 'media_index', @@ -2635,9 +2189,7 @@ class $SyncRulesTable extends SyncRules requiredDuringInsert: false, defaultValue: const Constant(0), ); - static const VerificationMeta _downloadFilterMeta = const VerificationMeta( - 'downloadFilter', - ); + static const VerificationMeta _downloadFilterMeta = const VerificationMeta('downloadFilter'); @override late final GeneratedColumn downloadFilter = GeneratedColumn( 'download_filter', @@ -2667,94 +2219,58 @@ class $SyncRulesTable extends SyncRules String get actualTableName => $name; static const String $name = 'sync_rules'; @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { + VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } if (data.containsKey('server_id')) { - context.handle( - _serverIdMeta, - serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta), - ); + context.handle(_serverIdMeta, serverId.isAcceptableOrUnknown(data['server_id']!, _serverIdMeta)); } else if (isInserting) { context.missing(_serverIdMeta); } if (data.containsKey('rating_key')) { - context.handle( - _ratingKeyMeta, - ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta), - ); + context.handle(_ratingKeyMeta, ratingKey.isAcceptableOrUnknown(data['rating_key']!, _ratingKeyMeta)); } else if (isInserting) { context.missing(_ratingKeyMeta); } if (data.containsKey('global_key')) { - context.handle( - _globalKeyMeta, - globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta), - ); + context.handle(_globalKeyMeta, globalKey.isAcceptableOrUnknown(data['global_key']!, _globalKeyMeta)); } else if (isInserting) { context.missing(_globalKeyMeta); } if (data.containsKey('target_type')) { - context.handle( - _targetTypeMeta, - targetType.isAcceptableOrUnknown(data['target_type']!, _targetTypeMeta), - ); + context.handle(_targetTypeMeta, targetType.isAcceptableOrUnknown(data['target_type']!, _targetTypeMeta)); } else if (isInserting) { context.missing(_targetTypeMeta); } if (data.containsKey('episode_count')) { - context.handle( - _episodeCountMeta, - episodeCount.isAcceptableOrUnknown( - data['episode_count']!, - _episodeCountMeta, - ), - ); + context.handle(_episodeCountMeta, episodeCount.isAcceptableOrUnknown(data['episode_count']!, _episodeCountMeta)); } else if (isInserting) { context.missing(_episodeCountMeta); } if (data.containsKey('enabled')) { - context.handle( - _enabledMeta, - enabled.isAcceptableOrUnknown(data['enabled']!, _enabledMeta), - ); + context.handle(_enabledMeta, enabled.isAcceptableOrUnknown(data['enabled']!, _enabledMeta)); } if (data.containsKey('created_at')) { - context.handle( - _createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), - ); + context.handle(_createdAtMeta, createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } else if (isInserting) { context.missing(_createdAtMeta); } if (data.containsKey('last_executed_at')) { context.handle( _lastExecutedAtMeta, - lastExecutedAt.isAcceptableOrUnknown( - data['last_executed_at']!, - _lastExecutedAtMeta, - ), + lastExecutedAt.isAcceptableOrUnknown(data['last_executed_at']!, _lastExecutedAtMeta), ); } if (data.containsKey('media_index')) { - context.handle( - _mediaIndexMeta, - mediaIndex.isAcceptableOrUnknown(data['media_index']!, _mediaIndexMeta), - ); + context.handle(_mediaIndexMeta, mediaIndex.isAcceptableOrUnknown(data['media_index']!, _mediaIndexMeta)); } if (data.containsKey('download_filter')) { context.handle( _downloadFilterMeta, - downloadFilter.isAcceptableOrUnknown( - data['download_filter']!, - _downloadFilterMeta, - ), + downloadFilter.isAcceptableOrUnknown(data['download_filter']!, _downloadFilterMeta), ); } return context; @@ -2766,46 +2282,16 @@ class $SyncRulesTable extends SyncRules SyncRuleItem map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return SyncRuleItem( - id: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}id'], - )!, - serverId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}server_id'], - )!, - ratingKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}rating_key'], - )!, - globalKey: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}global_key'], - )!, - targetType: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}target_type'], - )!, - episodeCount: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}episode_count'], - )!, - enabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, - data['${effectivePrefix}enabled'], - )!, - createdAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}created_at'], - )!, - lastExecutedAt: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}last_executed_at'], - ), - mediaIndex: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}media_index'], - )!, + id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + serverId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}server_id'])!, + ratingKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}rating_key'])!, + globalKey: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}global_key'])!, + targetType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}target_type'])!, + episodeCount: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}episode_count'])!, + enabled: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}enabled'])!, + createdAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}created_at'])!, + lastExecutedAt: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}last_executed_at']), + mediaIndex: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}media_index'])!, downloadFilter: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}download_filter'], @@ -2873,18 +2359,13 @@ class SyncRuleItem extends DataClass implements Insertable { episodeCount: Value(episodeCount), enabled: Value(enabled), createdAt: Value(createdAt), - lastExecutedAt: lastExecutedAt == null && nullToAbsent - ? const Value.absent() - : Value(lastExecutedAt), + lastExecutedAt: lastExecutedAt == null && nullToAbsent ? const Value.absent() : Value(lastExecutedAt), mediaIndex: Value(mediaIndex), downloadFilter: Value(downloadFilter), ); } - factory SyncRuleItem.fromJson( - Map json, { - ValueSerializer? serializer, - }) { + factory SyncRuleItem.fromJson(Map json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return SyncRuleItem( id: serializer.fromJson(json['id']), @@ -2939,9 +2420,7 @@ class SyncRuleItem extends DataClass implements Insertable { episodeCount: episodeCount ?? this.episodeCount, enabled: enabled ?? this.enabled, createdAt: createdAt ?? this.createdAt, - lastExecutedAt: lastExecutedAt.present - ? lastExecutedAt.value - : this.lastExecutedAt, + lastExecutedAt: lastExecutedAt.present ? lastExecutedAt.value : this.lastExecutedAt, mediaIndex: mediaIndex ?? this.mediaIndex, downloadFilter: downloadFilter ?? this.downloadFilter, ); @@ -2951,23 +2430,13 @@ class SyncRuleItem extends DataClass implements Insertable { serverId: data.serverId.present ? data.serverId.value : this.serverId, ratingKey: data.ratingKey.present ? data.ratingKey.value : this.ratingKey, globalKey: data.globalKey.present ? data.globalKey.value : this.globalKey, - targetType: data.targetType.present - ? data.targetType.value - : this.targetType, - episodeCount: data.episodeCount.present - ? data.episodeCount.value - : this.episodeCount, + targetType: data.targetType.present ? data.targetType.value : this.targetType, + episodeCount: data.episodeCount.present ? data.episodeCount.value : this.episodeCount, enabled: data.enabled.present ? data.enabled.value : this.enabled, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - lastExecutedAt: data.lastExecutedAt.present - ? data.lastExecutedAt.value - : this.lastExecutedAt, - mediaIndex: data.mediaIndex.present - ? data.mediaIndex.value - : this.mediaIndex, - downloadFilter: data.downloadFilter.present - ? data.downloadFilter.value - : this.downloadFilter, + lastExecutedAt: data.lastExecutedAt.present ? data.lastExecutedAt.value : this.lastExecutedAt, + mediaIndex: data.mediaIndex.present ? data.mediaIndex.value : this.mediaIndex, + downloadFilter: data.downloadFilter.present ? data.downloadFilter.value : this.downloadFilter, ); } @@ -3180,13 +2649,10 @@ class SyncRulesCompanion extends UpdateCompanion { abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); - late final $DownloadedMediaTable downloadedMedia = $DownloadedMediaTable( - this, - ); + late final $DownloadedMediaTable downloadedMedia = $DownloadedMediaTable(this); late final $DownloadQueueTable downloadQueue = $DownloadQueueTable(this); late final $ApiCacheTable apiCache = $ApiCacheTable(this); - late final $OfflineWatchProgressTable offlineWatchProgress = - $OfflineWatchProgressTable(this); + late final $OfflineWatchProgressTable offlineWatchProgress = $OfflineWatchProgressTable(this); late final $SyncRulesTable syncRules = $SyncRulesTable(this); late final Index idxDownloadedMediaStatus = Index( 'idx_downloaded_media_status', @@ -3205,8 +2671,7 @@ abstract class _$AppDatabase extends GeneratedDatabase { 'CREATE INDEX idx_downloaded_media_grandparent ON downloaded_media (grandparent_rating_key)', ); @override - Iterable> get allTables => - allSchemaEntities.whereType>(); + Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ downloadedMedia, @@ -3264,8 +2729,7 @@ typedef $$DownloadedMediaTableUpdateCompanionBuilder = Value mediaIndex, }); -class $$DownloadedMediaTableFilterComposer - extends Composer<_$AppDatabase, $DownloadedMediaTable> { +class $$DownloadedMediaTableFilterComposer extends Composer<_$AppDatabase, $DownloadedMediaTable> { $$DownloadedMediaTableFilterComposer({ required super.$db, required super.$table, @@ -3273,99 +2737,60 @@ class $$DownloadedMediaTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get id => $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnFilters(column)); - ColumnFilters get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get type => $composableBuilder(column: $table.type, builder: (column) => ColumnFilters(column)); - ColumnFilters get parentRatingKey => $composableBuilder( - column: $table.parentRatingKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get parentRatingKey => + $composableBuilder(column: $table.parentRatingKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get grandparentRatingKey => $composableBuilder( - column: $table.grandparentRatingKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get grandparentRatingKey => + $composableBuilder(column: $table.grandparentRatingKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get status => $composableBuilder( - column: $table.status, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get status => + $composableBuilder(column: $table.status, builder: (column) => ColumnFilters(column)); - ColumnFilters get progress => $composableBuilder( - column: $table.progress, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get progress => + $composableBuilder(column: $table.progress, builder: (column) => ColumnFilters(column)); - ColumnFilters get totalBytes => $composableBuilder( - column: $table.totalBytes, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get totalBytes => + $composableBuilder(column: $table.totalBytes, builder: (column) => ColumnFilters(column)); - ColumnFilters get downloadedBytes => $composableBuilder( - column: $table.downloadedBytes, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get downloadedBytes => + $composableBuilder(column: $table.downloadedBytes, builder: (column) => ColumnFilters(column)); - ColumnFilters get videoFilePath => $composableBuilder( - column: $table.videoFilePath, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get videoFilePath => + $composableBuilder(column: $table.videoFilePath, builder: (column) => ColumnFilters(column)); - ColumnFilters get thumbPath => $composableBuilder( - column: $table.thumbPath, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get thumbPath => + $composableBuilder(column: $table.thumbPath, builder: (column) => ColumnFilters(column)); - ColumnFilters get downloadedAt => $composableBuilder( - column: $table.downloadedAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get downloadedAt => + $composableBuilder(column: $table.downloadedAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get errorMessage => $composableBuilder( - column: $table.errorMessage, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get errorMessage => + $composableBuilder(column: $table.errorMessage, builder: (column) => ColumnFilters(column)); - ColumnFilters get retryCount => $composableBuilder( - column: $table.retryCount, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get retryCount => + $composableBuilder(column: $table.retryCount, builder: (column) => ColumnFilters(column)); - ColumnFilters get bgTaskId => $composableBuilder( - column: $table.bgTaskId, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get bgTaskId => + $composableBuilder(column: $table.bgTaskId, builder: (column) => ColumnFilters(column)); - ColumnFilters get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get mediaIndex => + $composableBuilder(column: $table.mediaIndex, builder: (column) => ColumnFilters(column)); } -class $$DownloadedMediaTableOrderingComposer - extends Composer<_$AppDatabase, $DownloadedMediaTable> { +class $$DownloadedMediaTableOrderingComposer extends Composer<_$AppDatabase, $DownloadedMediaTable> { $$DownloadedMediaTableOrderingComposer({ required super.$db, required super.$table, @@ -3373,99 +2798,61 @@ class $$DownloadedMediaTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get id => $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get type => $composableBuilder( - column: $table.type, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get type => + $composableBuilder(column: $table.type, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get parentRatingKey => $composableBuilder( - column: $table.parentRatingKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get parentRatingKey => + $composableBuilder(column: $table.parentRatingKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get grandparentRatingKey => $composableBuilder( - column: $table.grandparentRatingKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get grandparentRatingKey => + $composableBuilder(column: $table.grandparentRatingKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get status => $composableBuilder( - column: $table.status, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get status => + $composableBuilder(column: $table.status, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get progress => $composableBuilder( - column: $table.progress, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get progress => + $composableBuilder(column: $table.progress, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get totalBytes => $composableBuilder( - column: $table.totalBytes, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get totalBytes => + $composableBuilder(column: $table.totalBytes, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get downloadedBytes => $composableBuilder( - column: $table.downloadedBytes, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get downloadedBytes => + $composableBuilder(column: $table.downloadedBytes, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get videoFilePath => $composableBuilder( - column: $table.videoFilePath, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get videoFilePath => + $composableBuilder(column: $table.videoFilePath, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get thumbPath => $composableBuilder( - column: $table.thumbPath, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get thumbPath => + $composableBuilder(column: $table.thumbPath, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get downloadedAt => $composableBuilder( - column: $table.downloadedAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get downloadedAt => + $composableBuilder(column: $table.downloadedAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get errorMessage => $composableBuilder( - column: $table.errorMessage, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get errorMessage => + $composableBuilder(column: $table.errorMessage, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get retryCount => $composableBuilder( - column: $table.retryCount, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get retryCount => + $composableBuilder(column: $table.retryCount, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get bgTaskId => $composableBuilder( - column: $table.bgTaskId, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get bgTaskId => + $composableBuilder(column: $table.bgTaskId, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get mediaIndex => + $composableBuilder(column: $table.mediaIndex, builder: (column) => ColumnOrderings(column)); } -class $$DownloadedMediaTableAnnotationComposer - extends Composer<_$AppDatabase, $DownloadedMediaTable> { +class $$DownloadedMediaTableAnnotationComposer extends Composer<_$AppDatabase, $DownloadedMediaTable> { $$DownloadedMediaTableAnnotationComposer({ required super.$db, required super.$table, @@ -3473,77 +2860,46 @@ class $$DownloadedMediaTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); + GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get serverId => - $composableBuilder(column: $table.serverId, builder: (column) => column); + GeneratedColumn get serverId => $composableBuilder(column: $table.serverId, builder: (column) => column); - GeneratedColumn get ratingKey => - $composableBuilder(column: $table.ratingKey, builder: (column) => column); + GeneratedColumn get ratingKey => $composableBuilder(column: $table.ratingKey, builder: (column) => column); - GeneratedColumn get globalKey => - $composableBuilder(column: $table.globalKey, builder: (column) => column); + GeneratedColumn get globalKey => $composableBuilder(column: $table.globalKey, builder: (column) => column); - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); + GeneratedColumn get type => $composableBuilder(column: $table.type, builder: (column) => column); - GeneratedColumn get parentRatingKey => $composableBuilder( - column: $table.parentRatingKey, - builder: (column) => column, - ); + GeneratedColumn get parentRatingKey => + $composableBuilder(column: $table.parentRatingKey, builder: (column) => column); - GeneratedColumn get grandparentRatingKey => $composableBuilder( - column: $table.grandparentRatingKey, - builder: (column) => column, - ); + GeneratedColumn get grandparentRatingKey => + $composableBuilder(column: $table.grandparentRatingKey, builder: (column) => column); - GeneratedColumn get status => - $composableBuilder(column: $table.status, builder: (column) => column); + GeneratedColumn get status => $composableBuilder(column: $table.status, builder: (column) => column); - GeneratedColumn get progress => - $composableBuilder(column: $table.progress, builder: (column) => column); + GeneratedColumn get progress => $composableBuilder(column: $table.progress, builder: (column) => column); - GeneratedColumn get totalBytes => $composableBuilder( - column: $table.totalBytes, - builder: (column) => column, - ); + GeneratedColumn get totalBytes => $composableBuilder(column: $table.totalBytes, builder: (column) => column); - GeneratedColumn get downloadedBytes => $composableBuilder( - column: $table.downloadedBytes, - builder: (column) => column, - ); + GeneratedColumn get downloadedBytes => + $composableBuilder(column: $table.downloadedBytes, builder: (column) => column); - GeneratedColumn get videoFilePath => $composableBuilder( - column: $table.videoFilePath, - builder: (column) => column, - ); + GeneratedColumn get videoFilePath => + $composableBuilder(column: $table.videoFilePath, builder: (column) => column); - GeneratedColumn get thumbPath => - $composableBuilder(column: $table.thumbPath, builder: (column) => column); + GeneratedColumn get thumbPath => $composableBuilder(column: $table.thumbPath, builder: (column) => column); - GeneratedColumn get downloadedAt => $composableBuilder( - column: $table.downloadedAt, - builder: (column) => column, - ); + GeneratedColumn get downloadedAt => $composableBuilder(column: $table.downloadedAt, builder: (column) => column); - GeneratedColumn get errorMessage => $composableBuilder( - column: $table.errorMessage, - builder: (column) => column, - ); + GeneratedColumn get errorMessage => + $composableBuilder(column: $table.errorMessage, builder: (column) => column); - GeneratedColumn get retryCount => $composableBuilder( - column: $table.retryCount, - builder: (column) => column, - ); + GeneratedColumn get retryCount => $composableBuilder(column: $table.retryCount, builder: (column) => column); - GeneratedColumn get bgTaskId => - $composableBuilder(column: $table.bgTaskId, builder: (column) => column); + GeneratedColumn get bgTaskId => $composableBuilder(column: $table.bgTaskId, builder: (column) => column); - GeneratedColumn get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => column, - ); + GeneratedColumn get mediaIndex => $composableBuilder(column: $table.mediaIndex, builder: (column) => column); } class $$DownloadedMediaTableTableManager @@ -3557,30 +2913,18 @@ class $$DownloadedMediaTableTableManager $$DownloadedMediaTableAnnotationComposer, $$DownloadedMediaTableCreateCompanionBuilder, $$DownloadedMediaTableUpdateCompanionBuilder, - ( - DownloadedMediaItem, - BaseReferences< - _$AppDatabase, - $DownloadedMediaTable, - DownloadedMediaItem - >, - ), + (DownloadedMediaItem, BaseReferences<_$AppDatabase, $DownloadedMediaTable, DownloadedMediaItem>), DownloadedMediaItem, PrefetchHooks Function() > { - $$DownloadedMediaTableTableManager( - _$AppDatabase db, - $DownloadedMediaTable table, - ) : super( + $$DownloadedMediaTableTableManager(_$AppDatabase db, $DownloadedMediaTable table) + : super( TableManagerState( db: db, table: table, - createFilteringComposer: () => - $$DownloadedMediaTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$DownloadedMediaTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$DownloadedMediaTableAnnotationComposer($db: db, $table: table), + createFilteringComposer: () => $$DownloadedMediaTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => $$DownloadedMediaTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => $$DownloadedMediaTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -3661,9 +3005,7 @@ class $$DownloadedMediaTableTableManager bgTaskId: bgTaskId, mediaIndex: mediaIndex, ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), + withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, ), ); @@ -3679,14 +3021,7 @@ typedef $$DownloadedMediaTableProcessedTableManager = $$DownloadedMediaTableAnnotationComposer, $$DownloadedMediaTableCreateCompanionBuilder, $$DownloadedMediaTableUpdateCompanionBuilder, - ( - DownloadedMediaItem, - BaseReferences< - _$AppDatabase, - $DownloadedMediaTable, - DownloadedMediaItem - >, - ), + (DownloadedMediaItem, BaseReferences<_$AppDatabase, $DownloadedMediaTable, DownloadedMediaItem>), DownloadedMediaItem, PrefetchHooks Function() >; @@ -3709,8 +3044,7 @@ typedef $$DownloadQueueTableUpdateCompanionBuilder = Value downloadArtwork, }); -class $$DownloadQueueTableFilterComposer - extends Composer<_$AppDatabase, $DownloadQueueTable> { +class $$DownloadQueueTableFilterComposer extends Composer<_$AppDatabase, $DownloadQueueTable> { $$DownloadQueueTableFilterComposer({ required super.$db, required super.$table, @@ -3718,39 +3052,25 @@ class $$DownloadQueueTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get id => $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get mediaGlobalKey => $composableBuilder( - column: $table.mediaGlobalKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get mediaGlobalKey => + $composableBuilder(column: $table.mediaGlobalKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get priority => $composableBuilder( - column: $table.priority, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get priority => + $composableBuilder(column: $table.priority, builder: (column) => ColumnFilters(column)); - ColumnFilters get addedAt => $composableBuilder( - column: $table.addedAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get addedAt => + $composableBuilder(column: $table.addedAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get downloadSubtitles => $composableBuilder( - column: $table.downloadSubtitles, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get downloadSubtitles => + $composableBuilder(column: $table.downloadSubtitles, builder: (column) => ColumnFilters(column)); - ColumnFilters get downloadArtwork => $composableBuilder( - column: $table.downloadArtwork, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get downloadArtwork => + $composableBuilder(column: $table.downloadArtwork, builder: (column) => ColumnFilters(column)); } -class $$DownloadQueueTableOrderingComposer - extends Composer<_$AppDatabase, $DownloadQueueTable> { +class $$DownloadQueueTableOrderingComposer extends Composer<_$AppDatabase, $DownloadQueueTable> { $$DownloadQueueTableOrderingComposer({ required super.$db, required super.$table, @@ -3758,39 +3078,25 @@ class $$DownloadQueueTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get id => $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get mediaGlobalKey => $composableBuilder( - column: $table.mediaGlobalKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get mediaGlobalKey => + $composableBuilder(column: $table.mediaGlobalKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get priority => $composableBuilder( - column: $table.priority, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get priority => + $composableBuilder(column: $table.priority, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get addedAt => $composableBuilder( - column: $table.addedAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get addedAt => + $composableBuilder(column: $table.addedAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get downloadSubtitles => $composableBuilder( - column: $table.downloadSubtitles, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get downloadSubtitles => + $composableBuilder(column: $table.downloadSubtitles, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get downloadArtwork => $composableBuilder( - column: $table.downloadArtwork, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get downloadArtwork => + $composableBuilder(column: $table.downloadArtwork, builder: (column) => ColumnOrderings(column)); } -class $$DownloadQueueTableAnnotationComposer - extends Composer<_$AppDatabase, $DownloadQueueTable> { +class $$DownloadQueueTableAnnotationComposer extends Composer<_$AppDatabase, $DownloadQueueTable> { $$DownloadQueueTableAnnotationComposer({ required super.$db, required super.$table, @@ -3798,29 +3104,20 @@ class $$DownloadQueueTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); + GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get mediaGlobalKey => $composableBuilder( - column: $table.mediaGlobalKey, - builder: (column) => column, - ); + GeneratedColumn get mediaGlobalKey => + $composableBuilder(column: $table.mediaGlobalKey, builder: (column) => column); - GeneratedColumn get priority => - $composableBuilder(column: $table.priority, builder: (column) => column); + GeneratedColumn get priority => $composableBuilder(column: $table.priority, builder: (column) => column); - GeneratedColumn get addedAt => - $composableBuilder(column: $table.addedAt, builder: (column) => column); + GeneratedColumn get addedAt => $composableBuilder(column: $table.addedAt, builder: (column) => column); - GeneratedColumn get downloadSubtitles => $composableBuilder( - column: $table.downloadSubtitles, - builder: (column) => column, - ); + GeneratedColumn get downloadSubtitles => + $composableBuilder(column: $table.downloadSubtitles, builder: (column) => column); - GeneratedColumn get downloadArtwork => $composableBuilder( - column: $table.downloadArtwork, - builder: (column) => column, - ); + GeneratedColumn get downloadArtwork => + $composableBuilder(column: $table.downloadArtwork, builder: (column) => column); } class $$DownloadQueueTableTableManager @@ -3834,14 +3131,7 @@ class $$DownloadQueueTableTableManager $$DownloadQueueTableAnnotationComposer, $$DownloadQueueTableCreateCompanionBuilder, $$DownloadQueueTableUpdateCompanionBuilder, - ( - DownloadQueueItem, - BaseReferences< - _$AppDatabase, - $DownloadQueueTable, - DownloadQueueItem - >, - ), + (DownloadQueueItem, BaseReferences<_$AppDatabase, $DownloadQueueTable, DownloadQueueItem>), DownloadQueueItem, PrefetchHooks Function() > { @@ -3850,12 +3140,9 @@ class $$DownloadQueueTableTableManager TableManagerState( db: db, table: table, - createFilteringComposer: () => - $$DownloadQueueTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$DownloadQueueTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$DownloadQueueTableAnnotationComposer($db: db, $table: table), + createFilteringComposer: () => $$DownloadQueueTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => $$DownloadQueueTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => $$DownloadQueueTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -3888,9 +3175,7 @@ class $$DownloadQueueTableTableManager downloadSubtitles: downloadSubtitles, downloadArtwork: downloadArtwork, ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), + withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, ), ); @@ -3906,10 +3191,7 @@ typedef $$DownloadQueueTableProcessedTableManager = $$DownloadQueueTableAnnotationComposer, $$DownloadQueueTableCreateCompanionBuilder, $$DownloadQueueTableUpdateCompanionBuilder, - ( - DownloadQueueItem, - BaseReferences<_$AppDatabase, $DownloadQueueTable, DownloadQueueItem>, - ), + (DownloadQueueItem, BaseReferences<_$AppDatabase, $DownloadQueueTable, DownloadQueueItem>), DownloadQueueItem, PrefetchHooks Function() >; @@ -3930,8 +3212,7 @@ typedef $$ApiCacheTableUpdateCompanionBuilder = Value rowid, }); -class $$ApiCacheTableFilterComposer - extends Composer<_$AppDatabase, $ApiCacheTable> { +class $$ApiCacheTableFilterComposer extends Composer<_$AppDatabase, $ApiCacheTable> { $$ApiCacheTableFilterComposer({ required super.$db, required super.$table, @@ -3939,29 +3220,19 @@ class $$ApiCacheTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get cacheKey => $composableBuilder( - column: $table.cacheKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get cacheKey => + $composableBuilder(column: $table.cacheKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get data => $composableBuilder( - column: $table.data, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get data => $composableBuilder(column: $table.data, builder: (column) => ColumnFilters(column)); - ColumnFilters get pinned => $composableBuilder( - column: $table.pinned, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get pinned => + $composableBuilder(column: $table.pinned, builder: (column) => ColumnFilters(column)); - ColumnFilters get cachedAt => $composableBuilder( - column: $table.cachedAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get cachedAt => + $composableBuilder(column: $table.cachedAt, builder: (column) => ColumnFilters(column)); } -class $$ApiCacheTableOrderingComposer - extends Composer<_$AppDatabase, $ApiCacheTable> { +class $$ApiCacheTableOrderingComposer extends Composer<_$AppDatabase, $ApiCacheTable> { $$ApiCacheTableOrderingComposer({ required super.$db, required super.$table, @@ -3969,29 +3240,20 @@ class $$ApiCacheTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get cacheKey => $composableBuilder( - column: $table.cacheKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get cacheKey => + $composableBuilder(column: $table.cacheKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get data => $composableBuilder( - column: $table.data, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get data => + $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get pinned => $composableBuilder( - column: $table.pinned, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get pinned => + $composableBuilder(column: $table.pinned, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get cachedAt => $composableBuilder( - column: $table.cachedAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get cachedAt => + $composableBuilder(column: $table.cachedAt, builder: (column) => ColumnOrderings(column)); } -class $$ApiCacheTableAnnotationComposer - extends Composer<_$AppDatabase, $ApiCacheTable> { +class $$ApiCacheTableAnnotationComposer extends Composer<_$AppDatabase, $ApiCacheTable> { $$ApiCacheTableAnnotationComposer({ required super.$db, required super.$table, @@ -3999,17 +3261,13 @@ class $$ApiCacheTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get cacheKey => - $composableBuilder(column: $table.cacheKey, builder: (column) => column); + GeneratedColumn get cacheKey => $composableBuilder(column: $table.cacheKey, builder: (column) => column); - GeneratedColumn get data => - $composableBuilder(column: $table.data, builder: (column) => column); + GeneratedColumn get data => $composableBuilder(column: $table.data, builder: (column) => column); - GeneratedColumn get pinned => - $composableBuilder(column: $table.pinned, builder: (column) => column); + GeneratedColumn get pinned => $composableBuilder(column: $table.pinned, builder: (column) => column); - GeneratedColumn get cachedAt => - $composableBuilder(column: $table.cachedAt, builder: (column) => column); + GeneratedColumn get cachedAt => $composableBuilder(column: $table.cachedAt, builder: (column) => column); } class $$ApiCacheTableTableManager @@ -4023,10 +3281,7 @@ class $$ApiCacheTableTableManager $$ApiCacheTableAnnotationComposer, $$ApiCacheTableCreateCompanionBuilder, $$ApiCacheTableUpdateCompanionBuilder, - ( - ApiCacheData, - BaseReferences<_$AppDatabase, $ApiCacheTable, ApiCacheData>, - ), + (ApiCacheData, BaseReferences<_$AppDatabase, $ApiCacheTable, ApiCacheData>), ApiCacheData, PrefetchHooks Function() > { @@ -4035,12 +3290,9 @@ class $$ApiCacheTableTableManager TableManagerState( db: db, table: table, - createFilteringComposer: () => - $$ApiCacheTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ApiCacheTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ApiCacheTableAnnotationComposer($db: db, $table: table), + createFilteringComposer: () => $$ApiCacheTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => $$ApiCacheTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => $$ApiCacheTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value cacheKey = const Value.absent(), @@ -4048,13 +3300,7 @@ class $$ApiCacheTableTableManager Value pinned = const Value.absent(), Value cachedAt = const Value.absent(), Value rowid = const Value.absent(), - }) => ApiCacheCompanion( - cacheKey: cacheKey, - data: data, - pinned: pinned, - cachedAt: cachedAt, - rowid: rowid, - ), + }) => ApiCacheCompanion(cacheKey: cacheKey, data: data, pinned: pinned, cachedAt: cachedAt, rowid: rowid), createCompanionCallback: ({ required String cacheKey, @@ -4069,9 +3315,7 @@ class $$ApiCacheTableTableManager cachedAt: cachedAt, rowid: rowid, ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), + withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, ), ); @@ -4087,10 +3331,7 @@ typedef $$ApiCacheTableProcessedTableManager = $$ApiCacheTableAnnotationComposer, $$ApiCacheTableCreateCompanionBuilder, $$ApiCacheTableUpdateCompanionBuilder, - ( - ApiCacheData, - BaseReferences<_$AppDatabase, $ApiCacheTable, ApiCacheData>, - ), + (ApiCacheData, BaseReferences<_$AppDatabase, $ApiCacheTable, ApiCacheData>), ApiCacheData, PrefetchHooks Function() >; @@ -4125,8 +3366,7 @@ typedef $$OfflineWatchProgressTableUpdateCompanionBuilder = Value lastError, }); -class $$OfflineWatchProgressTableFilterComposer - extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { +class $$OfflineWatchProgressTableFilterComposer extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { $$OfflineWatchProgressTableFilterComposer({ required super.$db, required super.$table, @@ -4134,69 +3374,43 @@ class $$OfflineWatchProgressTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get id => $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnFilters(column)); - ColumnFilters get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get actionType => $composableBuilder( - column: $table.actionType, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get actionType => + $composableBuilder(column: $table.actionType, builder: (column) => ColumnFilters(column)); - ColumnFilters get viewOffset => $composableBuilder( - column: $table.viewOffset, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get viewOffset => + $composableBuilder(column: $table.viewOffset, builder: (column) => ColumnFilters(column)); - ColumnFilters get duration => $composableBuilder( - column: $table.duration, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get duration => + $composableBuilder(column: $table.duration, builder: (column) => ColumnFilters(column)); - ColumnFilters get shouldMarkWatched => $composableBuilder( - column: $table.shouldMarkWatched, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get shouldMarkWatched => + $composableBuilder(column: $table.shouldMarkWatched, builder: (column) => ColumnFilters(column)); - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get syncAttempts => $composableBuilder( - column: $table.syncAttempts, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get syncAttempts => + $composableBuilder(column: $table.syncAttempts, builder: (column) => ColumnFilters(column)); - ColumnFilters get lastError => $composableBuilder( - column: $table.lastError, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get lastError => + $composableBuilder(column: $table.lastError, builder: (column) => ColumnFilters(column)); } -class $$OfflineWatchProgressTableOrderingComposer - extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { +class $$OfflineWatchProgressTableOrderingComposer extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { $$OfflineWatchProgressTableOrderingComposer({ required super.$db, required super.$table, @@ -4204,69 +3418,43 @@ class $$OfflineWatchProgressTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get id => $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get actionType => $composableBuilder( - column: $table.actionType, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get actionType => + $composableBuilder(column: $table.actionType, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get viewOffset => $composableBuilder( - column: $table.viewOffset, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get viewOffset => + $composableBuilder(column: $table.viewOffset, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get duration => $composableBuilder( - column: $table.duration, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get duration => + $composableBuilder(column: $table.duration, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get shouldMarkWatched => $composableBuilder( - column: $table.shouldMarkWatched, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get shouldMarkWatched => + $composableBuilder(column: $table.shouldMarkWatched, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get syncAttempts => $composableBuilder( - column: $table.syncAttempts, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get syncAttempts => + $composableBuilder(column: $table.syncAttempts, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get lastError => $composableBuilder( - column: $table.lastError, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get lastError => + $composableBuilder(column: $table.lastError, builder: (column) => ColumnOrderings(column)); } -class $$OfflineWatchProgressTableAnnotationComposer - extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { +class $$OfflineWatchProgressTableAnnotationComposer extends Composer<_$AppDatabase, $OfflineWatchProgressTable> { $$OfflineWatchProgressTableAnnotationComposer({ required super.$db, required super.$table, @@ -4274,49 +3462,30 @@ class $$OfflineWatchProgressTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); + GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get serverId => - $composableBuilder(column: $table.serverId, builder: (column) => column); + GeneratedColumn get serverId => $composableBuilder(column: $table.serverId, builder: (column) => column); - GeneratedColumn get ratingKey => - $composableBuilder(column: $table.ratingKey, builder: (column) => column); + GeneratedColumn get ratingKey => $composableBuilder(column: $table.ratingKey, builder: (column) => column); - GeneratedColumn get globalKey => - $composableBuilder(column: $table.globalKey, builder: (column) => column); + GeneratedColumn get globalKey => $composableBuilder(column: $table.globalKey, builder: (column) => column); - GeneratedColumn get actionType => $composableBuilder( - column: $table.actionType, - builder: (column) => column, - ); + GeneratedColumn get actionType => $composableBuilder(column: $table.actionType, builder: (column) => column); - GeneratedColumn get viewOffset => $composableBuilder( - column: $table.viewOffset, - builder: (column) => column, - ); + GeneratedColumn get viewOffset => $composableBuilder(column: $table.viewOffset, builder: (column) => column); - GeneratedColumn get duration => - $composableBuilder(column: $table.duration, builder: (column) => column); + GeneratedColumn get duration => $composableBuilder(column: $table.duration, builder: (column) => column); - GeneratedColumn get shouldMarkWatched => $composableBuilder( - column: $table.shouldMarkWatched, - builder: (column) => column, - ); + GeneratedColumn get shouldMarkWatched => + $composableBuilder(column: $table.shouldMarkWatched, builder: (column) => column); - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); + GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); - GeneratedColumn get syncAttempts => $composableBuilder( - column: $table.syncAttempts, - builder: (column) => column, - ); + GeneratedColumn get syncAttempts => $composableBuilder(column: $table.syncAttempts, builder: (column) => column); - GeneratedColumn get lastError => - $composableBuilder(column: $table.lastError, builder: (column) => column); + GeneratedColumn get lastError => $composableBuilder(column: $table.lastError, builder: (column) => column); } class $$OfflineWatchProgressTableTableManager @@ -4332,34 +3501,19 @@ class $$OfflineWatchProgressTableTableManager $$OfflineWatchProgressTableUpdateCompanionBuilder, ( OfflineWatchProgressItem, - BaseReferences< - _$AppDatabase, - $OfflineWatchProgressTable, - OfflineWatchProgressItem - >, + BaseReferences<_$AppDatabase, $OfflineWatchProgressTable, OfflineWatchProgressItem>, ), OfflineWatchProgressItem, PrefetchHooks Function() > { - $$OfflineWatchProgressTableTableManager( - _$AppDatabase db, - $OfflineWatchProgressTable table, - ) : super( + $$OfflineWatchProgressTableTableManager(_$AppDatabase db, $OfflineWatchProgressTable table) + : super( TableManagerState( db: db, table: table, - createFilteringComposer: () => - $$OfflineWatchProgressTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$OfflineWatchProgressTableOrderingComposer( - $db: db, - $table: table, - ), - createComputedFieldComposer: () => - $$OfflineWatchProgressTableAnnotationComposer( - $db: db, - $table: table, - ), + createFilteringComposer: () => $$OfflineWatchProgressTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => $$OfflineWatchProgressTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => $$OfflineWatchProgressTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -4416,9 +3570,7 @@ class $$OfflineWatchProgressTableTableManager syncAttempts: syncAttempts, lastError: lastError, ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), + withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, ), ); @@ -4434,14 +3586,7 @@ typedef $$OfflineWatchProgressTableProcessedTableManager = $$OfflineWatchProgressTableAnnotationComposer, $$OfflineWatchProgressTableCreateCompanionBuilder, $$OfflineWatchProgressTableUpdateCompanionBuilder, - ( - OfflineWatchProgressItem, - BaseReferences< - _$AppDatabase, - $OfflineWatchProgressTable, - OfflineWatchProgressItem - >, - ), + (OfflineWatchProgressItem, BaseReferences<_$AppDatabase, $OfflineWatchProgressTable, OfflineWatchProgressItem>), OfflineWatchProgressItem, PrefetchHooks Function() >; @@ -4474,8 +3619,7 @@ typedef $$SyncRulesTableUpdateCompanionBuilder = Value downloadFilter, }); -class $$SyncRulesTableFilterComposer - extends Composer<_$AppDatabase, $SyncRulesTable> { +class $$SyncRulesTableFilterComposer extends Composer<_$AppDatabase, $SyncRulesTable> { $$SyncRulesTableFilterComposer({ required super.$db, required super.$table, @@ -4483,64 +3627,40 @@ class $$SyncRulesTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get id => $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnFilters(column)); - ColumnFilters get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnFilters(column)); - ColumnFilters get targetType => $composableBuilder( - column: $table.targetType, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get targetType => + $composableBuilder(column: $table.targetType, builder: (column) => ColumnFilters(column)); - ColumnFilters get episodeCount => $composableBuilder( - column: $table.episodeCount, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get episodeCount => + $composableBuilder(column: $table.episodeCount, builder: (column) => ColumnFilters(column)); - ColumnFilters get enabled => $composableBuilder( - column: $table.enabled, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get enabled => + $composableBuilder(column: $table.enabled, builder: (column) => ColumnFilters(column)); - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get lastExecutedAt => $composableBuilder( - column: $table.lastExecutedAt, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get lastExecutedAt => + $composableBuilder(column: $table.lastExecutedAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get mediaIndex => + $composableBuilder(column: $table.mediaIndex, builder: (column) => ColumnFilters(column)); - ColumnFilters get downloadFilter => $composableBuilder( - column: $table.downloadFilter, - builder: (column) => ColumnFilters(column), - ); + ColumnFilters get downloadFilter => + $composableBuilder(column: $table.downloadFilter, builder: (column) => ColumnFilters(column)); } -class $$SyncRulesTableOrderingComposer - extends Composer<_$AppDatabase, $SyncRulesTable> { +class $$SyncRulesTableOrderingComposer extends Composer<_$AppDatabase, $SyncRulesTable> { $$SyncRulesTableOrderingComposer({ required super.$db, required super.$table, @@ -4548,64 +3668,40 @@ class $$SyncRulesTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get id => $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get serverId => $composableBuilder( - column: $table.serverId, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get serverId => + $composableBuilder(column: $table.serverId, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get ratingKey => $composableBuilder( - column: $table.ratingKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get ratingKey => + $composableBuilder(column: $table.ratingKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get globalKey => $composableBuilder( - column: $table.globalKey, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get globalKey => + $composableBuilder(column: $table.globalKey, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get targetType => $composableBuilder( - column: $table.targetType, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get targetType => + $composableBuilder(column: $table.targetType, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get episodeCount => $composableBuilder( - column: $table.episodeCount, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get episodeCount => + $composableBuilder(column: $table.episodeCount, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get enabled => $composableBuilder( - column: $table.enabled, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get enabled => + $composableBuilder(column: $table.enabled, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get lastExecutedAt => $composableBuilder( - column: $table.lastExecutedAt, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get lastExecutedAt => + $composableBuilder(column: $table.lastExecutedAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get mediaIndex => + $composableBuilder(column: $table.mediaIndex, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get downloadFilter => $composableBuilder( - column: $table.downloadFilter, - builder: (column) => ColumnOrderings(column), - ); + ColumnOrderings get downloadFilter => + $composableBuilder(column: $table.downloadFilter, builder: (column) => ColumnOrderings(column)); } -class $$SyncRulesTableAnnotationComposer - extends Composer<_$AppDatabase, $SyncRulesTable> { +class $$SyncRulesTableAnnotationComposer extends Composer<_$AppDatabase, $SyncRulesTable> { $$SyncRulesTableAnnotationComposer({ required super.$db, required super.$table, @@ -4613,48 +3709,29 @@ class $$SyncRulesTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); + GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get serverId => - $composableBuilder(column: $table.serverId, builder: (column) => column); + GeneratedColumn get serverId => $composableBuilder(column: $table.serverId, builder: (column) => column); - GeneratedColumn get ratingKey => - $composableBuilder(column: $table.ratingKey, builder: (column) => column); + GeneratedColumn get ratingKey => $composableBuilder(column: $table.ratingKey, builder: (column) => column); - GeneratedColumn get globalKey => - $composableBuilder(column: $table.globalKey, builder: (column) => column); + GeneratedColumn get globalKey => $composableBuilder(column: $table.globalKey, builder: (column) => column); - GeneratedColumn get targetType => $composableBuilder( - column: $table.targetType, - builder: (column) => column, - ); + GeneratedColumn get targetType => $composableBuilder(column: $table.targetType, builder: (column) => column); - GeneratedColumn get episodeCount => $composableBuilder( - column: $table.episodeCount, - builder: (column) => column, - ); + GeneratedColumn get episodeCount => $composableBuilder(column: $table.episodeCount, builder: (column) => column); - GeneratedColumn get enabled => - $composableBuilder(column: $table.enabled, builder: (column) => column); + GeneratedColumn get enabled => $composableBuilder(column: $table.enabled, builder: (column) => column); - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); - GeneratedColumn get lastExecutedAt => $composableBuilder( - column: $table.lastExecutedAt, - builder: (column) => column, - ); + GeneratedColumn get lastExecutedAt => + $composableBuilder(column: $table.lastExecutedAt, builder: (column) => column); - GeneratedColumn get mediaIndex => $composableBuilder( - column: $table.mediaIndex, - builder: (column) => column, - ); + GeneratedColumn get mediaIndex => $composableBuilder(column: $table.mediaIndex, builder: (column) => column); - GeneratedColumn get downloadFilter => $composableBuilder( - column: $table.downloadFilter, - builder: (column) => column, - ); + GeneratedColumn get downloadFilter => + $composableBuilder(column: $table.downloadFilter, builder: (column) => column); } class $$SyncRulesTableTableManager @@ -4668,10 +3745,7 @@ class $$SyncRulesTableTableManager $$SyncRulesTableAnnotationComposer, $$SyncRulesTableCreateCompanionBuilder, $$SyncRulesTableUpdateCompanionBuilder, - ( - SyncRuleItem, - BaseReferences<_$AppDatabase, $SyncRulesTable, SyncRuleItem>, - ), + (SyncRuleItem, BaseReferences<_$AppDatabase, $SyncRulesTable, SyncRuleItem>), SyncRuleItem, PrefetchHooks Function() > { @@ -4680,12 +3754,9 @@ class $$SyncRulesTableTableManager TableManagerState( db: db, table: table, - createFilteringComposer: () => - $$SyncRulesTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$SyncRulesTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$SyncRulesTableAnnotationComposer($db: db, $table: table), + createFilteringComposer: () => $$SyncRulesTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => $$SyncRulesTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => $$SyncRulesTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -4738,9 +3809,7 @@ class $$SyncRulesTableTableManager mediaIndex: mediaIndex, downloadFilter: downloadFilter, ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), + withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, ), ); @@ -4756,10 +3825,7 @@ typedef $$SyncRulesTableProcessedTableManager = $$SyncRulesTableAnnotationComposer, $$SyncRulesTableCreateCompanionBuilder, $$SyncRulesTableUpdateCompanionBuilder, - ( - SyncRuleItem, - BaseReferences<_$AppDatabase, $SyncRulesTable, SyncRuleItem>, - ), + (SyncRuleItem, BaseReferences<_$AppDatabase, $SyncRulesTable, SyncRuleItem>), SyncRuleItem, PrefetchHooks Function() >; @@ -4769,12 +3835,9 @@ class $AppDatabaseManager { $AppDatabaseManager(this._db); $$DownloadedMediaTableTableManager get downloadedMedia => $$DownloadedMediaTableTableManager(_db, _db.downloadedMedia); - $$DownloadQueueTableTableManager get downloadQueue => - $$DownloadQueueTableTableManager(_db, _db.downloadQueue); - $$ApiCacheTableTableManager get apiCache => - $$ApiCacheTableTableManager(_db, _db.apiCache); + $$DownloadQueueTableTableManager get downloadQueue => $$DownloadQueueTableTableManager(_db, _db.downloadQueue); + $$ApiCacheTableTableManager get apiCache => $$ApiCacheTableTableManager(_db, _db.apiCache); $$OfflineWatchProgressTableTableManager get offlineWatchProgress => $$OfflineWatchProgressTableTableManager(_db, _db.offlineWatchProgress); - $$SyncRulesTableTableManager get syncRules => - $$SyncRulesTableTableManager(_db, _db.syncRules); + $$SyncRulesTableTableManager get syncRules => $$SyncRulesTableTableManager(_db, _db.syncRules); } diff --git a/lib/focus/focusable_wrapper.dart b/lib/focus/focusable_wrapper.dart index 1efce1372..93af8206b 100644 --- a/lib/focus/focusable_wrapper.dart +++ b/lib/focus/focusable_wrapper.dart @@ -136,8 +136,8 @@ class _FocusableWrapperState extends State with SingleTickerPr bool _ownsNode = false; bool _isFocused = false; - late AnimationController _animationController; - late Animation _scaleAnimation; + late final AnimationController _animationController; + late final Animation _scaleAnimation; // Long-press detection for SELECT key Timer? _longPressTimer; diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index 174073aff..d85af4d25 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Fejl ved indlæsning af serie", "errorLoadingSeason": "Fejl ved indlæsning af sæson", "musicNotSupported": "Musikafspilning understøttes endnu ikke", + "noDescriptionAvailable": "Ingen beskrivelse tilgængelig", + "noProfilesAvailable": "Ingen profiler tilgængelige", + "contactAdminForProfiles": "Kontakt din Plex-administrator for at tilføje profiler", + "unableToDetermineLibrarySection": "Kan ikke bestemme biblioteksafdeling for dette element", "logsCleared": "Logs ryddet", "logsCopied": "Logs kopieret til udklipsholder", "noLogsAvailable": "Ingen logs tilgængelige", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index 9098eb6fb..ce22e280d 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Fehler beim Laden der Serie", "errorLoadingSeason": "Fehler beim Laden der Staffel", "musicNotSupported": "Musikwiedergabe wird noch nicht unterstützt", + "noDescriptionAvailable": "Keine Beschreibung verfügbar", + "noProfilesAvailable": "Keine Profile verfügbar", + "contactAdminForProfiles": "Kontaktiere deinen Plex-Administrator, um Profile hinzuzufügen", + "unableToDetermineLibrarySection": "Bibliotheksbereich für dieses Element kann nicht ermittelt werden", "logsCleared": "Protokolle gelöscht", "logsCopied": "Protokolle in Zwischenablage kopiert", "noLogsAvailable": "Keine Protokolle verfügbar", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index 5e21f818d..0e6adf1d1 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Error loading series", "errorLoadingSeason": "Error loading season", "musicNotSupported": "Music playback is not yet supported", + "noDescriptionAvailable": "No description available", + "noProfilesAvailable": "No profiles available", + "contactAdminForProfiles": "Contact your Plex administrator to add profiles", + "unableToDetermineLibrarySection": "Unable to determine library section for this item", "logsCleared": "Logs cleared", "logsCopied": "Logs copied to clipboard", "noLogsAvailable": "No logs available", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index c03ece000..59391fa44 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Error al cargar la serie", "errorLoadingSeason": "Error al cargar la temporada", "musicNotSupported": "La reproducción de música aún no está soportada", + "noDescriptionAvailable": "No hay descripción disponible", + "noProfilesAvailable": "No hay perfiles disponibles", + "contactAdminForProfiles": "Contacta con tu administrador de Plex para añadir perfiles", + "unableToDetermineLibrarySection": "No se puede determinar la sección de biblioteca para este elemento", "logsCleared": "Logs borrados", "logsCopied": "Logs copiados al portapapeles", "noLogsAvailable": "No hay logs disponibles", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 2d02cce9c..9efecd1c2 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Erreur lors du chargement de la série", "errorLoadingSeason": "Erreur lors du chargement de la saison", "musicNotSupported": "La lecture de musique n'est pas encore prise en charge", + "noDescriptionAvailable": "Aucune description disponible", + "noProfilesAvailable": "Aucun profil disponible", + "contactAdminForProfiles": "Contactez votre administrateur Plex pour ajouter des profils", + "unableToDetermineLibrarySection": "Impossible de déterminer la section de la bibliothèque pour cet élément", "logsCleared": "Logs effacés", "logsCopied": "Logs copiés dans le presse-papier", "noLogsAvailable": "Aucun log disponible", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index a30eb9ef3..5a923c76a 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Errore caricamento serie", "errorLoadingSeason": "Errore caricamento stagione", "musicNotSupported": "La riproduzione musicale non è ancora supportata", + "noDescriptionAvailable": "Nessuna descrizione disponibile", + "noProfilesAvailable": "Nessun profilo disponibile", + "contactAdminForProfiles": "Contatta il tuo amministratore Plex per aggiungere profili", + "unableToDetermineLibrarySection": "Impossibile determinare la sezione della libreria per questo elemento", "logsCleared": "Log eliminati", "logsCopied": "Log copiati negli appunti", "noLogsAvailable": "Nessun log disponibile", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 1df528456..4c3c55367 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "シリーズの読み込みエラー", "errorLoadingSeason": "シーズンの読み込みエラー", "musicNotSupported": "音楽の再生はまだサポートされていません", + "noDescriptionAvailable": "説明はありません", + "noProfilesAvailable": "利用可能なプロフィールがありません", + "contactAdminForProfiles": "プロフィールを追加するにはPlex管理者にお問い合わせください", + "unableToDetermineLibrarySection": "このアイテムのライブラリセクションを判別できません", "logsCleared": "ログをクリアしました", "logsCopied": "ログをクリップボードにコピーしました", "noLogsAvailable": "ログがありません", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index 942561522..1ee09fc6d 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "시리즈 로딩 중 오류", "errorLoadingSeason": "시즌 로딩 중 오류", "musicNotSupported": "음악 재생 미지원", + "noDescriptionAvailable": "설명이 없습니다", + "noProfilesAvailable": "사용 가능한 프로필이 없습니다", + "contactAdminForProfiles": "프로필을 추가하려면 Plex 관리자에게 문의하세요", + "unableToDetermineLibrarySection": "이 항목의 라이브러리 섹션을 확인할 수 없습니다", "logsCleared": "로그가 삭제 되었습니다", "logsCopied": "로그가 클립보드에 복사 되었습니다", "noLogsAvailable": "사용 가능한 로그가 없습니다", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index 0633fb03c..00285bcef 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Feil ved lasting av serie", "errorLoadingSeason": "Feil ved lasting av sesong", "musicNotSupported": "Musikkavspilling støttes ikke ennå", + "noDescriptionAvailable": "Ingen beskrivelse tilgjengelig", + "noProfilesAvailable": "Ingen profiler tilgjengelige", + "contactAdminForProfiles": "Kontakt Plex-administratoren for å legge til profiler", + "unableToDetermineLibrarySection": "Kan ikke fastslå bibliotekseksjonen for dette elementet", "logsCleared": "Logger tømt", "logsCopied": "Logger kopiert til utklippstavle", "noLogsAvailable": "Ingen logger tilgjengelig", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index 502a0320e..646994ea9 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Fout bij laden serie", "errorLoadingSeason": "Fout bij laden seizoen", "musicNotSupported": "Muziek afspelen wordt nog niet ondersteund", + "noDescriptionAvailable": "Geen beschrijving beschikbaar", + "noProfilesAvailable": "Geen profielen beschikbaar", + "contactAdminForProfiles": "Neem contact op met je Plex-beheerder om profielen toe te voegen", + "unableToDetermineLibrarySection": "Kan bibliotheeksectie voor dit item niet bepalen", "logsCleared": "Logs gewist", "logsCopied": "Logs gekopieerd naar klembord", "noLogsAvailable": "Geen logs beschikbaar", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index cdd110793..0d45b565d 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Błąd ładowania serialu", "errorLoadingSeason": "Błąd ładowania sezonu", "musicNotSupported": "Odtwarzanie muzyki nie jest jeszcze obsługiwane", + "noDescriptionAvailable": "Brak dostępnego opisu", + "noProfilesAvailable": "Brak dostępnych profili", + "contactAdminForProfiles": "Skontaktuj się z administratorem Plex, aby dodać profile", + "unableToDetermineLibrarySection": "Nie można określić sekcji biblioteki dla tego elementu", "logsCleared": "Logi wyczyszczone", "logsCopied": "Logi skopiowane do schowka", "noLogsAvailable": "Brak dostępnych logów", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index 9b375d037..3c980d322 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Erro ao carregar série", "errorLoadingSeason": "Erro ao carregar temporada", "musicNotSupported": "Reprodução de música ainda não é suportada", + "noDescriptionAvailable": "Nenhuma descrição disponível", + "noProfilesAvailable": "Nenhum perfil disponível", + "contactAdminForProfiles": "Contacte o seu administrador Plex para adicionar perfis", + "unableToDetermineLibrarySection": "Não é possível determinar a secção da biblioteca para este item", "logsCleared": "Logs limpos", "logsCopied": "Logs copiados para a área de transferência", "noLogsAvailable": "Nenhum log disponível", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index d84399332..c160f1b3d 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Ошибка загрузки сериала", "errorLoadingSeason": "Ошибка загрузки сезона", "musicNotSupported": "Воспроизведение музыки пока не поддерживается", + "noDescriptionAvailable": "Описание недоступно", + "noProfilesAvailable": "Профили недоступны", + "contactAdminForProfiles": "Обратитесь к администратору Plex, чтобы добавить профили", + "unableToDetermineLibrarySection": "Не удаётся определить раздел библиотеки для этого элемента", "logsCleared": "Логи очищены", "logsCopied": "Логи скопированы в буфер обмена", "noLogsAvailable": "Логи отсутствуют", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 1b555bc44..5b9ce212d 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 15 -/// Strings: 13470 (898 per locale) +/// Strings: 13530 (902 per locale) /// -/// Built on 2026-04-20 at 16:17 UTC +/// Built on 2026-04-21 at 11:42 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/lib/i18n/strings_da.g.dart b/lib/i18n/strings_da.g.dart index b0f34ebe1..15f0d9b2d 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesDa implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Fejl ved indlæsning af serie'; @override String get errorLoadingSeason => 'Fejl ved indlæsning af sæson'; @override String get musicNotSupported => 'Musikafspilning understøttes endnu ikke'; + @override String get noDescriptionAvailable => 'Ingen beskrivelse tilgængelig'; + @override String get noProfilesAvailable => 'Ingen profiler tilgængelige'; + @override String get contactAdminForProfiles => 'Kontakt din Plex-administrator for at tilføje profiler'; + @override String get unableToDetermineLibrarySection => 'Kan ikke bestemme biblioteksafdeling for dette element'; @override String get logsCleared => 'Logs ryddet'; @override String get logsCopied => 'Logs kopieret til udklipsholder'; @override String get noLogsAvailable => 'Ingen logs tilgængelige'; @@ -1799,6 +1803,10 @@ extension on TranslationsDa { 'messages.errorLoadingSeries' => 'Fejl ved indlæsning af serie', 'messages.errorLoadingSeason' => 'Fejl ved indlæsning af sæson', 'messages.musicNotSupported' => 'Musikafspilning understøttes endnu ikke', + 'messages.noDescriptionAvailable' => 'Ingen beskrivelse tilgængelig', + 'messages.noProfilesAvailable' => 'Ingen profiler tilgængelige', + 'messages.contactAdminForProfiles' => 'Kontakt din Plex-administrator for at tilføje profiler', + 'messages.unableToDetermineLibrarySection' => 'Kan ikke bestemme biblioteksafdeling for dette element', 'messages.logsCleared' => 'Logs ryddet', 'messages.logsCopied' => 'Logs kopieret til udklipsholder', 'messages.noLogsAvailable' => 'Ingen logs tilgængelige', @@ -1913,12 +1921,12 @@ extension on TranslationsDa { 'libraries.noFoldersFound' => 'Ingen mapper fundet', 'libraries.folders' => 'mapper', 'libraries.tabs.recommended' => 'Anbefalet', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Gennemse', 'libraries.tabs.collections' => 'Samlinger', 'libraries.tabs.playlists' => 'Playlister', 'libraries.groupings.title' => 'Gruppering', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Alle', 'libraries.groupings.movies' => 'Film', 'libraries.groupings.shows' => 'TV-serier', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index 44d27a641..f1969b6c3 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesDe implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Fehler beim Laden der Serie'; @override String get errorLoadingSeason => 'Fehler beim Laden der Staffel'; @override String get musicNotSupported => 'Musikwiedergabe wird noch nicht unterstützt'; + @override String get noDescriptionAvailable => 'Keine Beschreibung verfügbar'; + @override String get noProfilesAvailable => 'Keine Profile verfügbar'; + @override String get contactAdminForProfiles => 'Kontaktiere deinen Plex-Administrator, um Profile hinzuzufügen'; + @override String get unableToDetermineLibrarySection => 'Bibliotheksbereich für dieses Element kann nicht ermittelt werden'; @override String get logsCleared => 'Protokolle gelöscht'; @override String get logsCopied => 'Protokolle in Zwischenablage kopiert'; @override String get noLogsAvailable => 'Keine Protokolle verfügbar'; @@ -1799,6 +1803,10 @@ extension on TranslationsDe { 'messages.errorLoadingSeries' => 'Fehler beim Laden der Serie', 'messages.errorLoadingSeason' => 'Fehler beim Laden der Staffel', 'messages.musicNotSupported' => 'Musikwiedergabe wird noch nicht unterstützt', + 'messages.noDescriptionAvailable' => 'Keine Beschreibung verfügbar', + 'messages.noProfilesAvailable' => 'Keine Profile verfügbar', + 'messages.contactAdminForProfiles' => 'Kontaktiere deinen Plex-Administrator, um Profile hinzuzufügen', + 'messages.unableToDetermineLibrarySection' => 'Bibliotheksbereich für dieses Element kann nicht ermittelt werden', 'messages.logsCleared' => 'Protokolle gelöscht', 'messages.logsCopied' => 'Protokolle in Zwischenablage kopiert', 'messages.noLogsAvailable' => 'Keine Protokolle verfügbar', @@ -1913,12 +1921,12 @@ extension on TranslationsDe { 'libraries.noFoldersFound' => 'Keine Ordner gefunden', 'libraries.folders' => 'Ordner', 'libraries.tabs.recommended' => 'Empfohlen', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Durchsuchen', 'libraries.tabs.collections' => 'Sammlungen', 'libraries.tabs.playlists' => 'Wiedergabelisten', 'libraries.groupings.title' => 'Gruppierung', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Alle', 'libraries.groupings.movies' => 'Filme', 'libraries.groupings.shows' => 'Serien', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 4498fabe5..dda5072a9 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -1317,6 +1317,18 @@ class TranslationsMessagesEn { /// en: 'Music playback is not yet supported' String get musicNotSupported => 'Music playback is not yet supported'; + /// en: 'No description available' + String get noDescriptionAvailable => 'No description available'; + + /// en: 'No profiles available' + String get noProfilesAvailable => 'No profiles available'; + + /// en: 'Contact your Plex administrator to add profiles' + String get contactAdminForProfiles => 'Contact your Plex administrator to add profiles'; + + /// en: 'Unable to determine library section for this item' + String get unableToDetermineLibrarySection => 'Unable to determine library section for this item'; + /// en: 'Logs cleared' String get logsCleared => 'Logs cleared'; @@ -3602,6 +3614,10 @@ extension on Translations { 'messages.errorLoadingSeries' => 'Error loading series', 'messages.errorLoadingSeason' => 'Error loading season', 'messages.musicNotSupported' => 'Music playback is not yet supported', + 'messages.noDescriptionAvailable' => 'No description available', + 'messages.noProfilesAvailable' => 'No profiles available', + 'messages.contactAdminForProfiles' => 'Contact your Plex administrator to add profiles', + 'messages.unableToDetermineLibrarySection' => 'Unable to determine library section for this item', 'messages.logsCleared' => 'Logs cleared', 'messages.logsCopied' => 'Logs copied to clipboard', 'messages.noLogsAvailable' => 'No logs available', @@ -3716,12 +3732,12 @@ extension on Translations { 'libraries.noFoldersFound' => 'No folders found', 'libraries.folders' => 'folders', 'libraries.tabs.recommended' => 'Recommended', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Browse', 'libraries.tabs.collections' => 'Collections', 'libraries.tabs.playlists' => 'Playlists', 'libraries.groupings.title' => 'Grouping', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'All', 'libraries.groupings.movies' => 'Movies', 'libraries.groupings.shows' => 'TV Shows', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index 1b8238894..542c1f3fa 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesEs implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Error al cargar la serie'; @override String get errorLoadingSeason => 'Error al cargar la temporada'; @override String get musicNotSupported => 'La reproducción de música aún no está soportada'; + @override String get noDescriptionAvailable => 'No hay descripción disponible'; + @override String get noProfilesAvailable => 'No hay perfiles disponibles'; + @override String get contactAdminForProfiles => 'Contacta con tu administrador de Plex para añadir perfiles'; + @override String get unableToDetermineLibrarySection => 'No se puede determinar la sección de biblioteca para este elemento'; @override String get logsCleared => 'Logs borrados'; @override String get logsCopied => 'Logs copiados al portapapeles'; @override String get noLogsAvailable => 'No hay logs disponibles'; @@ -1799,6 +1803,10 @@ extension on TranslationsEs { 'messages.errorLoadingSeries' => 'Error al cargar la serie', 'messages.errorLoadingSeason' => 'Error al cargar la temporada', 'messages.musicNotSupported' => 'La reproducción de música aún no está soportada', + 'messages.noDescriptionAvailable' => 'No hay descripción disponible', + 'messages.noProfilesAvailable' => 'No hay perfiles disponibles', + 'messages.contactAdminForProfiles' => 'Contacta con tu administrador de Plex para añadir perfiles', + 'messages.unableToDetermineLibrarySection' => 'No se puede determinar la sección de biblioteca para este elemento', 'messages.logsCleared' => 'Logs borrados', 'messages.logsCopied' => 'Logs copiados al portapapeles', 'messages.noLogsAvailable' => 'No hay logs disponibles', @@ -1913,12 +1921,12 @@ extension on TranslationsEs { 'libraries.noFoldersFound' => 'No se encontraron carpetas', 'libraries.folders' => 'carpetas', 'libraries.tabs.recommended' => 'Recomendado', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Explorar', 'libraries.tabs.collections' => 'Colecciones', 'libraries.tabs.playlists' => 'Listas', 'libraries.groupings.title' => 'Agrupación', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Todo', 'libraries.groupings.movies' => 'Películas', 'libraries.groupings.shows' => 'Series', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index b86a20937..303289053 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesFr implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Erreur lors du chargement de la série'; @override String get errorLoadingSeason => 'Erreur lors du chargement de la saison'; @override String get musicNotSupported => 'La lecture de musique n\'est pas encore prise en charge'; + @override String get noDescriptionAvailable => 'Aucune description disponible'; + @override String get noProfilesAvailable => 'Aucun profil disponible'; + @override String get contactAdminForProfiles => 'Contactez votre administrateur Plex pour ajouter des profils'; + @override String get unableToDetermineLibrarySection => 'Impossible de déterminer la section de la bibliothèque pour cet élément'; @override String get logsCleared => 'Logs effacés'; @override String get logsCopied => 'Logs copiés dans le presse-papier'; @override String get noLogsAvailable => 'Aucun log disponible'; @@ -1799,6 +1803,10 @@ extension on TranslationsFr { 'messages.errorLoadingSeries' => 'Erreur lors du chargement de la série', 'messages.errorLoadingSeason' => 'Erreur lors du chargement de la saison', 'messages.musicNotSupported' => 'La lecture de musique n\'est pas encore prise en charge', + 'messages.noDescriptionAvailable' => 'Aucune description disponible', + 'messages.noProfilesAvailable' => 'Aucun profil disponible', + 'messages.contactAdminForProfiles' => 'Contactez votre administrateur Plex pour ajouter des profils', + 'messages.unableToDetermineLibrarySection' => 'Impossible de déterminer la section de la bibliothèque pour cet élément', 'messages.logsCleared' => 'Logs effacés', 'messages.logsCopied' => 'Logs copiés dans le presse-papier', 'messages.noLogsAvailable' => 'Aucun log disponible', @@ -1913,12 +1921,12 @@ extension on TranslationsFr { 'libraries.noFoldersFound' => 'Aucun dossier trouvé', 'libraries.folders' => 'dossiers', 'libraries.tabs.recommended' => 'Recommandé', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Parcourir', 'libraries.tabs.collections' => 'Collections', 'libraries.tabs.playlists' => 'Playlists', 'libraries.groupings.title' => 'Regroupement', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Tous', 'libraries.groupings.movies' => 'Films', 'libraries.groupings.shows' => 'Show TV', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index b5b1c4097..b6486f9f9 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesIt implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Errore caricamento serie'; @override String get errorLoadingSeason => 'Errore caricamento stagione'; @override String get musicNotSupported => 'La riproduzione musicale non è ancora supportata'; + @override String get noDescriptionAvailable => 'Nessuna descrizione disponibile'; + @override String get noProfilesAvailable => 'Nessun profilo disponibile'; + @override String get contactAdminForProfiles => 'Contatta il tuo amministratore Plex per aggiungere profili'; + @override String get unableToDetermineLibrarySection => 'Impossibile determinare la sezione della libreria per questo elemento'; @override String get logsCleared => 'Log eliminati'; @override String get logsCopied => 'Log copiati negli appunti'; @override String get noLogsAvailable => 'Nessun log disponibile'; @@ -1799,6 +1803,10 @@ extension on TranslationsIt { 'messages.errorLoadingSeries' => 'Errore caricamento serie', 'messages.errorLoadingSeason' => 'Errore caricamento stagione', 'messages.musicNotSupported' => 'La riproduzione musicale non è ancora supportata', + 'messages.noDescriptionAvailable' => 'Nessuna descrizione disponibile', + 'messages.noProfilesAvailable' => 'Nessun profilo disponibile', + 'messages.contactAdminForProfiles' => 'Contatta il tuo amministratore Plex per aggiungere profili', + 'messages.unableToDetermineLibrarySection' => 'Impossibile determinare la sezione della libreria per questo elemento', 'messages.logsCleared' => 'Log eliminati', 'messages.logsCopied' => 'Log copiati negli appunti', 'messages.noLogsAvailable' => 'Nessun log disponibile', @@ -1913,12 +1921,12 @@ extension on TranslationsIt { 'libraries.noFoldersFound' => 'Nessuna cartella trovata', 'libraries.folders' => 'cartelle', 'libraries.tabs.recommended' => 'Consigliati', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Esplora', 'libraries.tabs.collections' => 'Raccolte', 'libraries.tabs.playlists' => 'Playlist', 'libraries.groupings.title' => 'Raggruppamento', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Tutti', 'libraries.groupings.movies' => 'Film', 'libraries.groupings.shows' => 'Serie TV', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index 04d978e73..bb1e4971c 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesJa implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'シリーズの読み込みエラー'; @override String get errorLoadingSeason => 'シーズンの読み込みエラー'; @override String get musicNotSupported => '音楽の再生はまだサポートされていません'; + @override String get noDescriptionAvailable => '説明はありません'; + @override String get noProfilesAvailable => '利用可能なプロフィールがありません'; + @override String get contactAdminForProfiles => 'プロフィールを追加するにはPlex管理者にお問い合わせください'; + @override String get unableToDetermineLibrarySection => 'このアイテムのライブラリセクションを判別できません'; @override String get logsCleared => 'ログをクリアしました'; @override String get logsCopied => 'ログをクリップボードにコピーしました'; @override String get noLogsAvailable => 'ログがありません'; @@ -1799,6 +1803,10 @@ extension on TranslationsJa { 'messages.errorLoadingSeries' => 'シリーズの読み込みエラー', 'messages.errorLoadingSeason' => 'シーズンの読み込みエラー', 'messages.musicNotSupported' => '音楽の再生はまだサポートされていません', + 'messages.noDescriptionAvailable' => '説明はありません', + 'messages.noProfilesAvailable' => '利用可能なプロフィールがありません', + 'messages.contactAdminForProfiles' => 'プロフィールを追加するにはPlex管理者にお問い合わせください', + 'messages.unableToDetermineLibrarySection' => 'このアイテムのライブラリセクションを判別できません', 'messages.logsCleared' => 'ログをクリアしました', 'messages.logsCopied' => 'ログをクリップボードにコピーしました', 'messages.noLogsAvailable' => 'ログがありません', @@ -1913,12 +1921,12 @@ extension on TranslationsJa { 'libraries.noFoldersFound' => 'フォルダが見つかりません', 'libraries.folders' => 'フォルダ', 'libraries.tabs.recommended' => 'おすすめ', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'ブラウズ', 'libraries.tabs.collections' => 'コレクション', 'libraries.tabs.playlists' => 'プレイリスト', 'libraries.groupings.title' => 'グループ', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'すべて', 'libraries.groupings.movies' => '映画', 'libraries.groupings.shows' => 'テレビ番組', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index ed00c9be8..20dab85c3 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesKo implements TranslationsMessagesEn { @override String get errorLoadingSeries => '시리즈 로딩 중 오류'; @override String get errorLoadingSeason => '시즌 로딩 중 오류'; @override String get musicNotSupported => '음악 재생 미지원'; + @override String get noDescriptionAvailable => '설명이 없습니다'; + @override String get noProfilesAvailable => '사용 가능한 프로필이 없습니다'; + @override String get contactAdminForProfiles => '프로필을 추가하려면 Plex 관리자에게 문의하세요'; + @override String get unableToDetermineLibrarySection => '이 항목의 라이브러리 섹션을 확인할 수 없습니다'; @override String get logsCleared => '로그가 삭제 되었습니다'; @override String get logsCopied => '로그가 클립보드에 복사 되었습니다'; @override String get noLogsAvailable => '사용 가능한 로그가 없습니다'; @@ -1799,6 +1803,10 @@ extension on TranslationsKo { 'messages.errorLoadingSeries' => '시리즈 로딩 중 오류', 'messages.errorLoadingSeason' => '시즌 로딩 중 오류', 'messages.musicNotSupported' => '음악 재생 미지원', + 'messages.noDescriptionAvailable' => '설명이 없습니다', + 'messages.noProfilesAvailable' => '사용 가능한 프로필이 없습니다', + 'messages.contactAdminForProfiles' => '프로필을 추가하려면 Plex 관리자에게 문의하세요', + 'messages.unableToDetermineLibrarySection' => '이 항목의 라이브러리 섹션을 확인할 수 없습니다', 'messages.logsCleared' => '로그가 삭제 되었습니다', 'messages.logsCopied' => '로그가 클립보드에 복사 되었습니다', 'messages.noLogsAvailable' => '사용 가능한 로그가 없습니다', @@ -1913,12 +1921,12 @@ extension on TranslationsKo { 'libraries.noFoldersFound' => '폴더를 찾을 수 없습니다', 'libraries.folders' => '폴더', 'libraries.tabs.recommended' => '추천', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => '찾아보기', 'libraries.tabs.collections' => '컬렉션', 'libraries.tabs.playlists' => '재생 목록', 'libraries.groupings.title' => '그룹', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => '전체', 'libraries.groupings.movies' => '영화', 'libraries.groupings.shows' => 'TV 프로그램', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index 72bf4eb82..b8c6c3433 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesNb implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Feil ved lasting av serie'; @override String get errorLoadingSeason => 'Feil ved lasting av sesong'; @override String get musicNotSupported => 'Musikkavspilling støttes ikke ennå'; + @override String get noDescriptionAvailable => 'Ingen beskrivelse tilgjengelig'; + @override String get noProfilesAvailable => 'Ingen profiler tilgjengelige'; + @override String get contactAdminForProfiles => 'Kontakt Plex-administratoren for å legge til profiler'; + @override String get unableToDetermineLibrarySection => 'Kan ikke fastslå bibliotekseksjonen for dette elementet'; @override String get logsCleared => 'Logger tømt'; @override String get logsCopied => 'Logger kopiert til utklippstavle'; @override String get noLogsAvailable => 'Ingen logger tilgjengelig'; @@ -1799,6 +1803,10 @@ extension on TranslationsNb { 'messages.errorLoadingSeries' => 'Feil ved lasting av serie', 'messages.errorLoadingSeason' => 'Feil ved lasting av sesong', 'messages.musicNotSupported' => 'Musikkavspilling støttes ikke ennå', + 'messages.noDescriptionAvailable' => 'Ingen beskrivelse tilgjengelig', + 'messages.noProfilesAvailable' => 'Ingen profiler tilgjengelige', + 'messages.contactAdminForProfiles' => 'Kontakt Plex-administratoren for å legge til profiler', + 'messages.unableToDetermineLibrarySection' => 'Kan ikke fastslå bibliotekseksjonen for dette elementet', 'messages.logsCleared' => 'Logger tømt', 'messages.logsCopied' => 'Logger kopiert til utklippstavle', 'messages.noLogsAvailable' => 'Ingen logger tilgjengelig', @@ -1913,12 +1921,12 @@ extension on TranslationsNb { 'libraries.noFoldersFound' => 'Ingen mapper funnet', 'libraries.folders' => 'mapper', 'libraries.tabs.recommended' => 'Anbefalt', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Bla gjennom', 'libraries.tabs.collections' => 'Samlinger', 'libraries.tabs.playlists' => 'Spillelister', 'libraries.groupings.title' => 'Gruppering', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Alle', 'libraries.groupings.movies' => 'Filmer', 'libraries.groupings.shows' => 'TV-serier', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index caaec153f..8f0c17d5a 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesNl implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Fout bij laden serie'; @override String get errorLoadingSeason => 'Fout bij laden seizoen'; @override String get musicNotSupported => 'Muziek afspelen wordt nog niet ondersteund'; + @override String get noDescriptionAvailable => 'Geen beschrijving beschikbaar'; + @override String get noProfilesAvailable => 'Geen profielen beschikbaar'; + @override String get contactAdminForProfiles => 'Neem contact op met je Plex-beheerder om profielen toe te voegen'; + @override String get unableToDetermineLibrarySection => 'Kan bibliotheeksectie voor dit item niet bepalen'; @override String get logsCleared => 'Logs gewist'; @override String get logsCopied => 'Logs gekopieerd naar klembord'; @override String get noLogsAvailable => 'Geen logs beschikbaar'; @@ -1799,6 +1803,10 @@ extension on TranslationsNl { 'messages.errorLoadingSeries' => 'Fout bij laden serie', 'messages.errorLoadingSeason' => 'Fout bij laden seizoen', 'messages.musicNotSupported' => 'Muziek afspelen wordt nog niet ondersteund', + 'messages.noDescriptionAvailable' => 'Geen beschrijving beschikbaar', + 'messages.noProfilesAvailable' => 'Geen profielen beschikbaar', + 'messages.contactAdminForProfiles' => 'Neem contact op met je Plex-beheerder om profielen toe te voegen', + 'messages.unableToDetermineLibrarySection' => 'Kan bibliotheeksectie voor dit item niet bepalen', 'messages.logsCleared' => 'Logs gewist', 'messages.logsCopied' => 'Logs gekopieerd naar klembord', 'messages.noLogsAvailable' => 'Geen logs beschikbaar', @@ -1913,12 +1921,12 @@ extension on TranslationsNl { 'libraries.noFoldersFound' => 'Geen mappen gevonden', 'libraries.folders' => 'mappen', 'libraries.tabs.recommended' => 'Aanbevolen', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Bladeren', 'libraries.tabs.collections' => 'Collecties', 'libraries.tabs.playlists' => 'Afspeellijsten', 'libraries.groupings.title' => 'Groepering', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Alles', 'libraries.groupings.movies' => 'Films', 'libraries.groupings.shows' => 'Series', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index 88c53693c..6600e04c8 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesPl implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Błąd ładowania serialu'; @override String get errorLoadingSeason => 'Błąd ładowania sezonu'; @override String get musicNotSupported => 'Odtwarzanie muzyki nie jest jeszcze obsługiwane'; + @override String get noDescriptionAvailable => 'Brak dostępnego opisu'; + @override String get noProfilesAvailable => 'Brak dostępnych profili'; + @override String get contactAdminForProfiles => 'Skontaktuj się z administratorem Plex, aby dodać profile'; + @override String get unableToDetermineLibrarySection => 'Nie można określić sekcji biblioteki dla tego elementu'; @override String get logsCleared => 'Logi wyczyszczone'; @override String get logsCopied => 'Logi skopiowane do schowka'; @override String get noLogsAvailable => 'Brak dostępnych logów'; @@ -1799,6 +1803,10 @@ extension on TranslationsPl { 'messages.errorLoadingSeries' => 'Błąd ładowania serialu', 'messages.errorLoadingSeason' => 'Błąd ładowania sezonu', 'messages.musicNotSupported' => 'Odtwarzanie muzyki nie jest jeszcze obsługiwane', + 'messages.noDescriptionAvailable' => 'Brak dostępnego opisu', + 'messages.noProfilesAvailable' => 'Brak dostępnych profili', + 'messages.contactAdminForProfiles' => 'Skontaktuj się z administratorem Plex, aby dodać profile', + 'messages.unableToDetermineLibrarySection' => 'Nie można określić sekcji biblioteki dla tego elementu', 'messages.logsCleared' => 'Logi wyczyszczone', 'messages.logsCopied' => 'Logi skopiowane do schowka', 'messages.noLogsAvailable' => 'Brak dostępnych logów', @@ -1913,12 +1921,12 @@ extension on TranslationsPl { 'libraries.noFoldersFound' => 'Nie znaleziono folderów', 'libraries.folders' => 'foldery', 'libraries.tabs.recommended' => 'Rekomendowane', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Przeglądaj', 'libraries.tabs.collections' => 'Kolekcje', 'libraries.tabs.playlists' => 'Playlisty', 'libraries.groupings.title' => 'Grupowanie', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Wszystkie', 'libraries.groupings.movies' => 'Filmy', 'libraries.groupings.shows' => 'Seriale TV', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index 53edf2e43..90d1c5f72 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesPt implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Erro ao carregar série'; @override String get errorLoadingSeason => 'Erro ao carregar temporada'; @override String get musicNotSupported => 'Reprodução de música ainda não é suportada'; + @override String get noDescriptionAvailable => 'Nenhuma descrição disponível'; + @override String get noProfilesAvailable => 'Nenhum perfil disponível'; + @override String get contactAdminForProfiles => 'Contacte o seu administrador Plex para adicionar perfis'; + @override String get unableToDetermineLibrarySection => 'Não é possível determinar a secção da biblioteca para este item'; @override String get logsCleared => 'Logs limpos'; @override String get logsCopied => 'Logs copiados para a área de transferência'; @override String get noLogsAvailable => 'Nenhum log disponível'; @@ -1799,6 +1803,10 @@ extension on TranslationsPt { 'messages.errorLoadingSeries' => 'Erro ao carregar série', 'messages.errorLoadingSeason' => 'Erro ao carregar temporada', 'messages.musicNotSupported' => 'Reprodução de música ainda não é suportada', + 'messages.noDescriptionAvailable' => 'Nenhuma descrição disponível', + 'messages.noProfilesAvailable' => 'Nenhum perfil disponível', + 'messages.contactAdminForProfiles' => 'Contacte o seu administrador Plex para adicionar perfis', + 'messages.unableToDetermineLibrarySection' => 'Não é possível determinar a secção da biblioteca para este item', 'messages.logsCleared' => 'Logs limpos', 'messages.logsCopied' => 'Logs copiados para a área de transferência', 'messages.noLogsAvailable' => 'Nenhum log disponível', @@ -1913,12 +1921,12 @@ extension on TranslationsPt { 'libraries.noFoldersFound' => 'Nenhuma pasta encontrada', 'libraries.folders' => 'pastas', 'libraries.tabs.recommended' => 'Recomendados', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Navegar', 'libraries.tabs.collections' => 'Coleções', 'libraries.tabs.playlists' => 'Playlists', 'libraries.groupings.title' => 'Agrupamento', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Todos', 'libraries.groupings.movies' => 'Filmes', 'libraries.groupings.shows' => 'Séries de TV', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index 554946cac..4febc4f5e 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesRu implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Ошибка загрузки сериала'; @override String get errorLoadingSeason => 'Ошибка загрузки сезона'; @override String get musicNotSupported => 'Воспроизведение музыки пока не поддерживается'; + @override String get noDescriptionAvailable => 'Описание недоступно'; + @override String get noProfilesAvailable => 'Профили недоступны'; + @override String get contactAdminForProfiles => 'Обратитесь к администратору Plex, чтобы добавить профили'; + @override String get unableToDetermineLibrarySection => 'Не удаётся определить раздел библиотеки для этого элемента'; @override String get logsCleared => 'Логи очищены'; @override String get logsCopied => 'Логи скопированы в буфер обмена'; @override String get noLogsAvailable => 'Логи отсутствуют'; @@ -1799,6 +1803,10 @@ extension on TranslationsRu { 'messages.errorLoadingSeries' => 'Ошибка загрузки сериала', 'messages.errorLoadingSeason' => 'Ошибка загрузки сезона', 'messages.musicNotSupported' => 'Воспроизведение музыки пока не поддерживается', + 'messages.noDescriptionAvailable' => 'Описание недоступно', + 'messages.noProfilesAvailable' => 'Профили недоступны', + 'messages.contactAdminForProfiles' => 'Обратитесь к администратору Plex, чтобы добавить профили', + 'messages.unableToDetermineLibrarySection' => 'Не удаётся определить раздел библиотеки для этого элемента', 'messages.logsCleared' => 'Логи очищены', 'messages.logsCopied' => 'Логи скопированы в буфер обмена', 'messages.noLogsAvailable' => 'Логи отсутствуют', @@ -1913,12 +1921,12 @@ extension on TranslationsRu { 'libraries.noFoldersFound' => 'Папки не найдены', 'libraries.folders' => 'папки', 'libraries.tabs.recommended' => 'Рекомендуемые', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Обзор', 'libraries.tabs.collections' => 'Коллекции', 'libraries.tabs.playlists' => 'Плейлисты', 'libraries.groupings.title' => 'Группировка', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Все', 'libraries.groupings.movies' => 'Фильмы', 'libraries.groupings.shows' => 'Сериалы', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index 916777008..4b242ea87 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesSv implements TranslationsMessagesEn { @override String get errorLoadingSeries => 'Fel vid laddning av serie'; @override String get errorLoadingSeason => 'Fel vid laddning av säsong'; @override String get musicNotSupported => 'Musikuppspelning stöds inte ännu'; + @override String get noDescriptionAvailable => 'Ingen beskrivning tillgänglig'; + @override String get noProfilesAvailable => 'Inga profiler tillgängliga'; + @override String get contactAdminForProfiles => 'Kontakta din Plex-administratör för att lägga till profiler'; + @override String get unableToDetermineLibrarySection => 'Kan inte avgöra biblioteksavdelningen för detta objekt'; @override String get logsCleared => 'Loggar rensade'; @override String get logsCopied => 'Loggar kopierade till urklipp'; @override String get noLogsAvailable => 'Inga loggar tillgängliga'; @@ -1799,6 +1803,10 @@ extension on TranslationsSv { 'messages.errorLoadingSeries' => 'Fel vid laddning av serie', 'messages.errorLoadingSeason' => 'Fel vid laddning av säsong', 'messages.musicNotSupported' => 'Musikuppspelning stöds inte ännu', + 'messages.noDescriptionAvailable' => 'Ingen beskrivning tillgänglig', + 'messages.noProfilesAvailable' => 'Inga profiler tillgängliga', + 'messages.contactAdminForProfiles' => 'Kontakta din Plex-administratör för att lägga till profiler', + 'messages.unableToDetermineLibrarySection' => 'Kan inte avgöra biblioteksavdelningen för detta objekt', 'messages.logsCleared' => 'Loggar rensade', 'messages.logsCopied' => 'Loggar kopierade till urklipp', 'messages.noLogsAvailable' => 'Inga loggar tillgängliga', @@ -1913,12 +1921,12 @@ extension on TranslationsSv { 'libraries.noFoldersFound' => 'Inga mappar hittades', 'libraries.folders' => 'mappar', 'libraries.tabs.recommended' => 'Rekommenderat', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => 'Bläddra', 'libraries.tabs.collections' => 'Samlingar', 'libraries.tabs.playlists' => 'Spellistor', 'libraries.groupings.title' => 'Gruppering', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => 'Alla', 'libraries.groupings.movies' => 'Filmer', 'libraries.groupings.shows' => 'Serier', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index 95fdc15d4..4695bc762 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -579,6 +579,10 @@ class _TranslationsMessagesZh implements TranslationsMessagesEn { @override String get errorLoadingSeries => '加载系列时出错'; @override String get errorLoadingSeason => '加载季时出错'; @override String get musicNotSupported => '尚不支持播放音乐'; + @override String get noDescriptionAvailable => '暂无描述'; + @override String get noProfilesAvailable => '没有可用的用户'; + @override String get contactAdminForProfiles => '请联系您的 Plex 管理员添加用户'; + @override String get unableToDetermineLibrarySection => '无法确定此项目的库分区'; @override String get logsCleared => '日志已清除'; @override String get logsCopied => '日志已复制到剪贴板'; @override String get noLogsAvailable => '没有可用日志'; @@ -1799,6 +1803,10 @@ extension on TranslationsZh { 'messages.errorLoadingSeries' => '加载系列时出错', 'messages.errorLoadingSeason' => '加载季时出错', 'messages.musicNotSupported' => '尚不支持播放音乐', + 'messages.noDescriptionAvailable' => '暂无描述', + 'messages.noProfilesAvailable' => '没有可用的用户', + 'messages.contactAdminForProfiles' => '请联系您的 Plex 管理员添加用户', + 'messages.unableToDetermineLibrarySection' => '无法确定此项目的库分区', 'messages.logsCleared' => '日志已清除', 'messages.logsCopied' => '日志已复制到剪贴板', 'messages.noLogsAvailable' => '没有可用日志', @@ -1913,12 +1921,12 @@ extension on TranslationsZh { 'libraries.noFoldersFound' => '未找到文件夹', 'libraries.folders' => '文件夹', 'libraries.tabs.recommended' => '推荐', + _ => null, + } ?? switch (path) { 'libraries.tabs.browse' => '浏览', 'libraries.tabs.collections' => '合集', 'libraries.tabs.playlists' => '播放列表', 'libraries.groupings.title' => '分组', - _ => null, - } ?? switch (path) { 'libraries.groupings.all' => '全部', 'libraries.groupings.movies' => '电影', 'libraries.groupings.shows' => '剧集', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index 50036b7a3..199da5758 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "Fel vid laddning av serie", "errorLoadingSeason": "Fel vid laddning av säsong", "musicNotSupported": "Musikuppspelning stöds inte ännu", + "noDescriptionAvailable": "Ingen beskrivning tillgänglig", + "noProfilesAvailable": "Inga profiler tillgängliga", + "contactAdminForProfiles": "Kontakta din Plex-administratör för att lägga till profiler", + "unableToDetermineLibrarySection": "Kan inte avgöra biblioteksavdelningen för detta objekt", "logsCleared": "Loggar rensade", "logsCopied": "Loggar kopierade till urklipp", "noLogsAvailable": "Inga loggar tillgängliga", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index 6c4fdf2c0..abd177416 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -426,6 +426,10 @@ "errorLoadingSeries": "加载系列时出错", "errorLoadingSeason": "加载季时出错", "musicNotSupported": "尚不支持播放音乐", + "noDescriptionAvailable": "暂无描述", + "noProfilesAvailable": "没有可用的用户", + "contactAdminForProfiles": "请联系您的 Plex 管理员添加用户", + "unableToDetermineLibrarySection": "无法确定此项目的库分区", "logsCleared": "日志已清除", "logsCopied": "日志已复制到剪贴板", "noLogsAvailable": "没有可用日志", diff --git a/lib/main.dart b/lib/main.dart index 1a708769d..d43681a6d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -638,6 +638,10 @@ class _MainAppState extends State with WidgetsBindingObserver { _startConnectivitySyncTrigger(downloadProvider); + // Thread the offline flag into services so queue/resume paths can + // short-circuit instead of hitting the network and failing. + downloadProvider.setOfflineSource(offlineModeProvider); + _offlineWatchSyncService.startConnectivityMonitoring(offlineModeProvider); return _offlineWatchSyncService; }, diff --git a/lib/mixins/tab_navigation_mixin.dart b/lib/mixins/tab_navigation_mixin.dart index 6f26cab85..5dce3ff80 100644 --- a/lib/mixins/tab_navigation_mixin.dart +++ b/lib/mixins/tab_navigation_mixin.dart @@ -14,7 +14,7 @@ import '../widgets/focusable_tab_chip.dart'; /// /// Subclasses must provide [tabChipFocusNodes] — one [FocusNode] per tab. mixin TabNavigationMixin on State, TickerProviderStateMixin { - late TabController tabController; + late final TabController tabController; /// When true, suppress auto-focus in tabs (used when navigating via tab bar). bool suppressAutoFocus = false; diff --git a/lib/models/companion_remote/remote_session.g.dart b/lib/models/companion_remote/remote_session.g.dart index ee6f3c4e8..ca1084a84 100644 --- a/lib/models/companion_remote/remote_session.g.dart +++ b/lib/models/companion_remote/remote_session.g.dart @@ -10,62 +10,44 @@ RemoteDevice _$RemoteDeviceFromJson(Map json) => RemoteDevice( id: json['id'] as String, name: json['name'] as String, platform: json['platform'] as String, - connectedAt: json['connectedAt'] == null - ? null - : DateTime.parse(json['connectedAt'] as String), - capabilities: (json['capabilities'] as Map?)?.map( - (k, e) => MapEntry(k, e as bool), - ), + connectedAt: json['connectedAt'] == null ? null : DateTime.parse(json['connectedAt'] as String), + capabilities: (json['capabilities'] as Map?)?.map((k, e) => MapEntry(k, e as bool)), ); -Map _$RemoteDeviceToJson(RemoteDevice instance) => - { - 'id': instance.id, - 'name': instance.name, - 'platform': instance.platform, - 'connectedAt': instance.connectedAt.toIso8601String(), - 'capabilities': instance.capabilities, - }; - -RemoteSession _$RemoteSessionFromJson(Map json) => - RemoteSession( - role: $enumDecode( - _$RemoteSessionRoleEnumMap, - json['role'], - unknownValue: RemoteSessionRole.remote, - ), - status: - $enumDecodeNullable( - _$RemoteSessionStatusEnumMap, - json['status'], - unknownValue: RemoteSessionStatus.disconnected, - ) ?? - RemoteSessionStatus.disconnected, - connectedDevice: json['connectedDevice'] == null - ? null - : RemoteDevice.fromJson( - json['connectedDevice'] as Map, - ), - createdAt: json['createdAt'] == null - ? null - : DateTime.parse(json['createdAt'] as String), - errorMessage: json['errorMessage'] as String?, - ); - -Map _$RemoteSessionToJson(RemoteSession instance) => - { - 'role': _$RemoteSessionRoleEnumMap[instance.role]!, - 'status': _$RemoteSessionStatusEnumMap[instance.status]!, - 'connectedDevice': instance.connectedDevice, - 'createdAt': instance.createdAt.toIso8601String(), - 'errorMessage': instance.errorMessage, - }; - -const _$RemoteSessionRoleEnumMap = { - RemoteSessionRole.host: 'host', - RemoteSessionRole.remote: 'remote', +Map _$RemoteDeviceToJson(RemoteDevice instance) => { + 'id': instance.id, + 'name': instance.name, + 'platform': instance.platform, + 'connectedAt': instance.connectedAt.toIso8601String(), + 'capabilities': instance.capabilities, }; +RemoteSession _$RemoteSessionFromJson(Map json) => RemoteSession( + role: $enumDecode(_$RemoteSessionRoleEnumMap, json['role'], unknownValue: RemoteSessionRole.remote), + status: + $enumDecodeNullable( + _$RemoteSessionStatusEnumMap, + json['status'], + unknownValue: RemoteSessionStatus.disconnected, + ) ?? + RemoteSessionStatus.disconnected, + connectedDevice: json['connectedDevice'] == null + ? null + : RemoteDevice.fromJson(json['connectedDevice'] as Map), + createdAt: json['createdAt'] == null ? null : DateTime.parse(json['createdAt'] as String), + errorMessage: json['errorMessage'] as String?, +); + +Map _$RemoteSessionToJson(RemoteSession instance) => { + 'role': _$RemoteSessionRoleEnumMap[instance.role]!, + 'status': _$RemoteSessionStatusEnumMap[instance.status]!, + 'connectedDevice': instance.connectedDevice, + 'createdAt': instance.createdAt.toIso8601String(), + 'errorMessage': instance.errorMessage, +}; + +const _$RemoteSessionRoleEnumMap = {RemoteSessionRole.host: 'host', RemoteSessionRole.remote: 'remote'}; + const _$RemoteSessionStatusEnumMap = { RemoteSessionStatus.disconnected: 'disconnected', RemoteSessionStatus.connecting: 'connecting', diff --git a/lib/models/play_queue_response.g.dart b/lib/models/play_queue_response.g.dart index 576098f07..70e8cd5aa 100644 --- a/lib/models/play_queue_response.g.dart +++ b/lib/models/play_queue_response.g.dart @@ -6,21 +6,15 @@ part of 'play_queue_response.dart'; // JsonSerializableGenerator // ************************************************************************** -PlayQueueResponse _$PlayQueueResponseFromJson(Map json) => - PlayQueueResponse( - playQueueID: (json['playQueueID'] as num).toInt(), - playQueueSelectedItemID: (json['playQueueSelectedItemID'] as num?) - ?.toInt(), - playQueueSelectedItemOffset: (json['playQueueSelectedItemOffset'] as num?) - ?.toInt(), - playQueueSelectedMetadataItemID: - json['playQueueSelectedMetadataItemID'] as String?, - playQueueShuffled: flexibleBool(json['playQueueShuffled']), - playQueueSourceURI: json['playQueueSourceURI'] as String?, - playQueueTotalCount: (json['playQueueTotalCount'] as num?)?.toInt(), - playQueueVersion: (json['playQueueVersion'] as num).toInt(), - size: (json['size'] as num?)?.toInt(), - items: (json['Metadata'] as List?) - ?.map((e) => PlexMetadata.fromJson(e as Map)) - .toList(), - ); +PlayQueueResponse _$PlayQueueResponseFromJson(Map json) => PlayQueueResponse( + playQueueID: (json['playQueueID'] as num).toInt(), + playQueueSelectedItemID: (json['playQueueSelectedItemID'] as num?)?.toInt(), + playQueueSelectedItemOffset: (json['playQueueSelectedItemOffset'] as num?)?.toInt(), + playQueueSelectedMetadataItemID: json['playQueueSelectedMetadataItemID'] as String?, + playQueueShuffled: flexibleBool(json['playQueueShuffled']), + playQueueSourceURI: json['playQueueSourceURI'] as String?, + playQueueTotalCount: (json['playQueueTotalCount'] as num?)?.toInt(), + playQueueVersion: (json['playQueueVersion'] as num).toInt(), + size: (json['size'] as num?)?.toInt(), + items: (json['Metadata'] as List?)?.map((e) => PlexMetadata.fromJson(e as Map)).toList(), +); diff --git a/lib/models/plex_library.g.dart b/lib/models/plex_library.g.dart index c527e046d..37a7f43eb 100644 --- a/lib/models/plex_library.g.dart +++ b/lib/models/plex_library.g.dart @@ -19,16 +19,15 @@ PlexLibrary _$PlexLibraryFromJson(Map json) => PlexLibrary( hidden: (json['hidden'] as num?)?.toInt(), ); -Map _$PlexLibraryToJson(PlexLibrary instance) => - { - 'key': instance.key, - 'title': instance.title, - 'type': instance.type, - 'agent': instance.agent, - 'scanner': instance.scanner, - 'language': instance.language, - 'uuid': instance.uuid, - 'updatedAt': instance.updatedAt, - 'createdAt': instance.createdAt, - 'hidden': instance.hidden, - }; +Map _$PlexLibraryToJson(PlexLibrary instance) => { + 'key': instance.key, + 'title': instance.title, + 'type': instance.type, + 'agent': instance.agent, + 'scanner': instance.scanner, + 'language': instance.language, + 'uuid': instance.uuid, + 'updatedAt': instance.updatedAt, + 'createdAt': instance.createdAt, + 'hidden': instance.hidden, +}; diff --git a/lib/models/plex_metadata.g.dart b/lib/models/plex_metadata.g.dart index 43d0be10a..cd168233a 100644 --- a/lib/models/plex_metadata.g.dart +++ b/lib/models/plex_metadata.g.dart @@ -42,9 +42,7 @@ PlexMetadata _$PlexMetadataFromJson(Map json) => PlexMetadata( leafCount: (json['leafCount'] as num?)?.toInt(), viewedLeafCount: (json['viewedLeafCount'] as num?)?.toInt(), childCount: flexibleInt(json['childCount']), - role: (json['Role'] as List?) - ?.map((e) => PlexRole.fromJson(e as Map)) - .toList(), + role: (json['Role'] as List?)?.map((e) => PlexRole.fromJson(e as Map)).toList(), mediaVersions: (json['Media'] as List?) ?.map((e) => PlexMediaVersion.fromJson(e as Map)) .toList(), @@ -76,59 +74,58 @@ PlexMetadata _$PlexMetadataFromJson(Map json) => PlexMetadata( backgroundSquare: json['backgroundSquare'] as String?, ); -Map _$PlexMetadataToJson(PlexMetadata instance) => - { - 'ratingKey': instance.ratingKey, - 'key': instance.key, - 'guid': instance.guid, - 'studio': instance.studio, - 'type': instance.type, - 'title': instance.title, - 'titleSort': instance.titleSort, - 'contentRating': instance.contentRating, - 'summary': instance.summary, - 'rating': instance.rating, - 'audienceRating': instance.audienceRating, - 'userRating': instance.userRating, - 'year': instance.year, - 'originallyAvailableAt': instance.originallyAvailableAt, - 'thumb': instance.thumb, - 'art': instance.art, - 'duration': instance.duration, - 'addedAt': instance.addedAt, - 'updatedAt': instance.updatedAt, - 'lastViewedAt': instance.lastViewedAt, - 'grandparentTitle': instance.grandparentTitle, - 'grandparentThumb': instance.grandparentThumb, - 'grandparentArt': instance.grandparentArt, - 'grandparentRatingKey': instance.grandparentRatingKey, - 'parentTitle': instance.parentTitle, - 'parentThumb': instance.parentThumb, - 'parentRatingKey': instance.parentRatingKey, - 'parentIndex': instance.parentIndex, - 'index': instance.index, - 'grandparentTheme': instance.grandparentTheme, - 'viewOffset': instance.viewOffset, - 'viewCount': instance.viewCount, - 'leafCount': instance.leafCount, - 'viewedLeafCount': instance.viewedLeafCount, - 'childCount': instance.childCount, - 'Role': instance.role, - 'audioLanguage': instance.audioLanguage, - 'subtitleLanguage': instance.subtitleLanguage, - 'subtitleMode': instance.subtitleMode, - 'playlistItemID': instance.playlistItemID, - 'playQueueItemID': instance.playQueueItemID, - 'librarySectionID': instance.librarySectionID, - 'librarySectionTitle': instance.librarySectionTitle, - 'ratingImage': instance.ratingImage, - 'audienceRatingImage': instance.audienceRatingImage, - 'tagline': instance.tagline, - 'originalTitle': instance.originalTitle, - 'editionTitle': instance.editionTitle, - 'subtype': instance.subtype, - 'extraType': instance.extraType, - 'primaryExtraKey': instance.primaryExtraKey, - 'clearLogo': instance.clearLogo, - 'backgroundSquare': instance.backgroundSquare, - }; +Map _$PlexMetadataToJson(PlexMetadata instance) => { + 'ratingKey': instance.ratingKey, + 'key': instance.key, + 'guid': instance.guid, + 'studio': instance.studio, + 'type': instance.type, + 'title': instance.title, + 'titleSort': instance.titleSort, + 'contentRating': instance.contentRating, + 'summary': instance.summary, + 'rating': instance.rating, + 'audienceRating': instance.audienceRating, + 'userRating': instance.userRating, + 'year': instance.year, + 'originallyAvailableAt': instance.originallyAvailableAt, + 'thumb': instance.thumb, + 'art': instance.art, + 'duration': instance.duration, + 'addedAt': instance.addedAt, + 'updatedAt': instance.updatedAt, + 'lastViewedAt': instance.lastViewedAt, + 'grandparentTitle': instance.grandparentTitle, + 'grandparentThumb': instance.grandparentThumb, + 'grandparentArt': instance.grandparentArt, + 'grandparentRatingKey': instance.grandparentRatingKey, + 'parentTitle': instance.parentTitle, + 'parentThumb': instance.parentThumb, + 'parentRatingKey': instance.parentRatingKey, + 'parentIndex': instance.parentIndex, + 'index': instance.index, + 'grandparentTheme': instance.grandparentTheme, + 'viewOffset': instance.viewOffset, + 'viewCount': instance.viewCount, + 'leafCount': instance.leafCount, + 'viewedLeafCount': instance.viewedLeafCount, + 'childCount': instance.childCount, + 'Role': instance.role, + 'audioLanguage': instance.audioLanguage, + 'subtitleLanguage': instance.subtitleLanguage, + 'subtitleMode': instance.subtitleMode, + 'playlistItemID': instance.playlistItemID, + 'playQueueItemID': instance.playQueueItemID, + 'librarySectionID': instance.librarySectionID, + 'librarySectionTitle': instance.librarySectionTitle, + 'ratingImage': instance.ratingImage, + 'audienceRatingImage': instance.audienceRatingImage, + 'tagline': instance.tagline, + 'originalTitle': instance.originalTitle, + 'editionTitle': instance.editionTitle, + 'subtype': instance.subtype, + 'extraType': instance.extraType, + 'primaryExtraKey': instance.primaryExtraKey, + 'clearLogo': instance.clearLogo, + 'backgroundSquare': instance.backgroundSquare, +}; diff --git a/lib/models/plex_playlist.g.dart b/lib/models/plex_playlist.g.dart index 63d5512b0..90dc322fa 100644 --- a/lib/models/plex_playlist.g.dart +++ b/lib/models/plex_playlist.g.dart @@ -26,23 +26,22 @@ PlexPlaylist _$PlexPlaylistFromJson(Map json) => PlexPlaylist( thumb: json['thumb'] as String?, ); -Map _$PlexPlaylistToJson(PlexPlaylist instance) => - { - 'ratingKey': instance.ratingKey, - 'key': instance.key, - 'type': instance.type, - 'title': instance.title, - 'summary': instance.summary, - 'smart': instance.smart, - 'playlistType': instance.playlistType, - 'duration': instance.duration, - 'leafCount': instance.leafCount, - 'composite': instance.composite, - 'addedAt': instance.addedAt, - 'updatedAt': instance.updatedAt, - 'lastViewedAt': instance.lastViewedAt, - 'viewCount': instance.viewCount, - 'content': instance.content, - 'guid': instance.guid, - 'thumb': instance.thumb, - }; +Map _$PlexPlaylistToJson(PlexPlaylist instance) => { + 'ratingKey': instance.ratingKey, + 'key': instance.key, + 'type': instance.type, + 'title': instance.title, + 'summary': instance.summary, + 'smart': instance.smart, + 'playlistType': instance.playlistType, + 'duration': instance.duration, + 'leafCount': instance.leafCount, + 'composite': instance.composite, + 'addedAt': instance.addedAt, + 'updatedAt': instance.updatedAt, + 'lastViewedAt': instance.lastViewedAt, + 'viewCount': instance.viewCount, + 'content': instance.content, + 'guid': instance.guid, + 'thumb': instance.thumb, +}; diff --git a/lib/mpv/models.dart b/lib/mpv/models.dart index 1ec311a96..c6196d9bc 100644 --- a/lib/mpv/models.dart +++ b/lib/mpv/models.dart @@ -288,11 +288,7 @@ class Media { /// Optional start position for playback. final Duration? start; - /// Optional pre-known video frame rate (from server metadata), used to drive - /// display refresh-rate matching before the first frame renders. - final double? fps; - - const Media(this.uri, {this.headers, this.start, this.fps}); + const Media(this.uri, {this.headers, this.start}); @override String toString() => 'Media($uri)'; diff --git a/lib/mpv/player/player_base.dart b/lib/mpv/player/player_base.dart index b87902e97..95a29491e 100644 --- a/lib/mpv/player/player_base.dart +++ b/lib/mpv/player/player_base.dart @@ -487,11 +487,6 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player { trackController.add(_state.track); } - /// Update the internal state. - void updateState(PlayerState Function(PlayerState) update) { - _state = update(_state); - } - @protected void clearTracks() { const empty = Tracks(); diff --git a/lib/providers/download_provider.dart b/lib/providers/download_provider.dart index c9829f07c..884d614a9 100644 --- a/lib/providers/download_provider.dart +++ b/lib/providers/download_provider.dart @@ -10,11 +10,13 @@ import '../database/app_database.dart'; import '../services/download_manager_service.dart'; import '../services/download_storage_service.dart'; import '../services/multi_server_manager.dart'; +import '../services/offline_mode_source.dart'; import '../services/storage_service.dart'; import '../services/plex_api_cache.dart'; import '../services/plex_client.dart'; import '../services/sync_rule_executor.dart'; import '../utils/app_logger.dart'; +import '../utils/episode_collection.dart'; import '../utils/global_key_utils.dart'; /// Filter mode for batch downloads (shows/seasons). @@ -67,6 +69,8 @@ class DownloadProvider extends ChangeNotifier { // Persistent sync rules: globalKey -> SyncRuleItem final Map _syncRules = {}; + OfflineModeSource? _offlineSource; + DownloadProvider({required DownloadManagerService downloadManager, required AppDatabase database}) : _downloadManager = downloadManager, _database = database, @@ -81,6 +85,15 @@ class DownloadProvider extends ChangeNotifier { _initFuture = _loadPersistedDownloads(); } + /// Inject the offline-mode source so queueing paths can short-circuit when + /// the device has no Plex connectivity. Propagates to the download manager + /// and the sync-rule executor so background paths see the same flag. + void setOfflineSource(OfflineModeSource? source) { + _offlineSource = source; + _downloadManager.setOfflineSource(source); + _syncRuleExecutor.setOfflineSource(source); + } + /// Ensures persisted downloads have been loaded from disk. Future ensureInitialized() => _initFuture; @@ -694,14 +707,22 @@ class DownloadProvider extends ChangeNotifier { // Hub items may have summary but the cache at /library/metadata/$ratingKey // won't have the full API response (with Media/Part data needed for video URL) // unless getMetadataWithImages has been called. + // + // Skip the fetch when offline — it would just fail. The partial metadata + // from whatever hub/grid invoked the queue is good enough to enqueue; the + // actual video URL resolves later when we're back online. PlexMetadata metadataToStore = metadata; - try { - final fullMetadata = await client.getMetadataWithImages(metadata.ratingKey); - if (fullMetadata != null) { - metadataToStore = fullMetadata.copyWith(serverId: metadata.serverId, serverName: metadata.serverName); + if (_offlineSource?.isOffline ?? false) { + appLogger.d('Offline — using partial metadata for ${metadata.ratingKey}'); + } else { + try { + final fullMetadata = await client.getMetadataWithImages(metadata.ratingKey); + if (fullMetadata != null) { + metadataToStore = fullMetadata.copyWith(serverId: metadata.serverId, serverName: metadata.serverName); + } + } catch (e) { + appLogger.w('Failed to fetch full metadata for ${metadata.ratingKey}, using partial', error: e); } - } catch (e) { - appLogger.w('Failed to fetch full metadata for ${metadata.ratingKey}, using partial', error: e); } // Smart version matching for series/season downloads @@ -796,29 +817,15 @@ class DownloadProvider extends ChangeNotifier { DownloadFilter filter = DownloadFilter.all, int? maxCount, }) async { - int count = 0; - final seasons = await client.getChildren(show.ratingKey); - await _storeLeafCount(show.globalKey, show); - - int? remaining = maxCount; - for (final season in seasons) { - if (season.type == 'season') { - if (remaining != null && remaining <= 0) break; - final seasonWithServer = _ensureServerId(season, show.serverId); - final queued = await _queueSeasonDownload( - seasonWithServer, - client, - versionConfig: versionConfig, - filter: filter, - maxCount: remaining, - ); - count += queued; - if (remaining != null) remaining -= queued; - } - } - - return count; + return _expandAndQueue( + container: show, + client: client, + versionConfig: versionConfig, + filter: filter, + maxCount: maxCount, + skipExisting: false, + ); } /// Queue all episodes from a season for download @@ -829,97 +836,81 @@ class DownloadProvider extends ChangeNotifier { DownloadFilter filter = DownloadFilter.all, int? maxCount, }) async { - int count = 0; - final episodes = await client.getChildren(season.ratingKey); - await _storeLeafCount(season.globalKey, season); - - for (final episode in episodes) { - if (episode.type == 'episode') { - if (filter == DownloadFilter.unwatched && episode.isWatched && !episode.hasActiveProgress) continue; - if (maxCount != null && count >= maxCount) break; - - final episodeWithServer = _ensureServerId(episode, season.serverId); - final queued = await _queueSingleDownload(episodeWithServer, client, versionConfig: versionConfig); - if (queued) count++; - } - } - - return count; + return _expandAndQueue( + container: season, + client: client, + versionConfig: versionConfig, + filter: filter, + maxCount: maxCount, + skipExisting: false, + ); } - /// Queue only the missing (not downloaded) episodes for a show/season - /// Used for resuming partial downloads - /// Returns the number of episodes queued + /// Queue only the missing (not downloaded) episodes for a show/season. + /// Used for resuming partial downloads. Returns the number of episodes queued. Future queueMissingEpisodes( PlexMetadata metadata, PlexClient client, { DownloadVersionConfig? versionConfig, }) async { final mt = metadata.mediaType; - - if (mt == PlexMediaType.show) { - return await _queueMissingShowEpisodes(metadata, client, versionConfig: versionConfig); - } else if (mt == PlexMediaType.season) { - return await _queueMissingSeasonEpisodes(metadata, client, versionConfig: versionConfig); - } else { + if (mt != PlexMediaType.show && mt != PlexMediaType.season) { throw Exception('queueMissingEpisodes only supports shows/seasons'); } + final queued = await _expandAndQueue( + container: metadata, + client: client, + versionConfig: versionConfig, + filter: DownloadFilter.all, + maxCount: null, + skipExisting: true, + ); + if (mt == PlexMediaType.show) { + appLogger.i('Queued $queued missing episodes for show ${metadata.title}'); + } + return queued; } - /// Queue missing episodes for a show - Future _queueMissingShowEpisodes( - PlexMetadata show, - PlexClient client, { - DownloadVersionConfig? versionConfig, + /// Shared expansion: fetch all episodes under [container] (show or season), + /// apply [filter] and optional [maxCount], optionally skip items already + /// queued/downloading/completed ([skipExisting]), and queue each one. + Future _expandAndQueue({ + required PlexMetadata container, + required PlexClient client, + required DownloadVersionConfig? versionConfig, + required DownloadFilter filter, + required int? maxCount, + required bool skipExisting, }) async { - int queuedCount = 0; - - final seasons = await client.getChildren(show.ratingKey); - - for (final season in seasons) { - if (season.type == 'season') { - final seasonWithServer = _ensureServerId(season, show.serverId); - queuedCount += await _queueMissingSeasonEpisodes(seasonWithServer, client, versionConfig: versionConfig); - } + final unwatchedOnly = filter == DownloadFilter.unwatched; + final episodes = []; + if (container.mediaType == PlexMediaType.show) { + await collectEpisodesForShow(client, container.ratingKey, unwatchedOnly: unwatchedOnly, out: episodes); + } else { + await collectEpisodesForSeason(client, container.ratingKey, unwatchedOnly: unwatchedOnly, out: episodes); } - appLogger.i('Queued $queuedCount missing episodes for show ${show.title}'); - return queuedCount; - } - - /// Queue missing episodes for a season - Future _queueMissingSeasonEpisodes( - PlexMetadata season, - PlexClient client, { - DownloadVersionConfig? versionConfig, - }) async { - int queuedCount = 0; - - final episodes = await client.getChildren(season.ratingKey); - + int count = 0; for (final episode in episodes) { - if (episode.type == 'episode') { - final episodeWithServer = _ensureServerId(episode, season.serverId); + if (maxCount != null && count >= maxCount) break; - final episodeGlobalKey = episodeWithServer.globalKey; + final episodeWithServer = _ensureServerId(episode, container.serverId); - // Only queue if NOT already downloaded or in progress - final progress = _downloads[episodeGlobalKey]; - if (progress == null || - (progress.status != DownloadStatus.completed && - progress.status != DownloadStatus.downloading && - progress.status != DownloadStatus.queued)) { - final queued = await _queueSingleDownload(episodeWithServer, client, versionConfig: versionConfig); - if (queued) { - queuedCount++; - appLogger.d('Queued missing episode: ${episode.title} ($episodeGlobalKey)'); - } + if (skipExisting) { + final progress = _downloads[episodeWithServer.globalKey]; + if (progress != null && + (progress.status == DownloadStatus.completed || + progress.status == DownloadStatus.downloading || + progress.status == DownloadStatus.queued)) { + continue; } } - } - return queuedCount; + final queued = await _queueSingleDownload(episodeWithServer, client, versionConfig: versionConfig); + if (queued) count++; + } + return count; } /// Pause a download (works for both downloading and queued items) diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart index 1945aa9ef..814e2693e 100644 --- a/lib/screens/auth_screen.dart +++ b/lib/screens/auth_screen.dart @@ -313,7 +313,7 @@ class _AuthScreenState extends State { @override Widget build(BuildContext context) { // Use two-column layout on desktop, single column on mobile - final isDesktop = MediaQuery.of(context).size.width > 700; + final isDesktop = MediaQuery.sizeOf(context).width > 700; return Scaffold( body: Center( diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index 03d6d4c8d..0c901da91 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -1197,7 +1197,7 @@ class _DiscoverScreenState extends State Widget _buildHeroSection() { final statusBarHeight = MediaQuery.of(context).padding.top; final useSideNav = PlatformDetector.shouldUseSideNavigation(context); - final heroHeight = useSideNav ? MediaQuery.of(context).size.height * 0.75 : 500 + statusBarHeight; + final heroHeight = useSideNav ? MediaQuery.sizeOf(context).height * 0.75 : 500 + statusBarHeight; return SliverToBoxAdapter( child: Focus( focusNode: _heroFocusNode, @@ -1317,7 +1317,7 @@ class _DiscoverScreenState extends State final heroClient = _getClientForItem(heroItem); final isEpisode = heroItem.isEpisode; final showName = heroItem.grandparentTitle ?? heroItem.displayTitle; - final screenWidth = MediaQuery.of(context).size.width; + final screenWidth = MediaQuery.sizeOf(context).width; final isLargeScreen = ScreenBreakpoints.isWideTabletOrLarger(screenWidth); // Determine content type label for chip @@ -1566,7 +1566,7 @@ class _DiscoverScreenState extends State TextSpan( text: heroItem.summary?.isNotEmpty == true ? heroItem.summary! - : 'No description available', + : t.messages.noDescriptionAvailable, ), ], ), diff --git a/lib/screens/libraries/adaptive_media_grid.dart b/lib/screens/libraries/adaptive_media_grid.dart index eaf6a9514..8e78b76e1 100644 --- a/lib/screens/libraries/adaptive_media_grid.dart +++ b/lib/screens/libraries/adaptive_media_grid.dart @@ -73,9 +73,10 @@ class AdaptiveMediaGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, settingsProvider, child) { - return _buildItemsView(context, settingsProvider.viewMode, settingsProvider.libraryDensity); + return Selector( + selector: (_, p) => (p.viewMode, p.libraryDensity), + builder: (context, slice, _) { + return _buildItemsView(context, slice.$1, slice.$2); }, ); } diff --git a/lib/screens/libraries/tabs/library_browse_tab.dart b/lib/screens/libraries/tabs/library_browse_tab.dart index 7e9044b2b..4b766907e 100644 --- a/lib/screens/libraries/tabs/library_browse_tab.dart +++ b/lib/screens/libraries/tabs/library_browse_tab.dart @@ -1228,7 +1228,7 @@ class _LibraryBrowseTabState extends BaseLibraryTabState(); final maxExtent = GridSizeCalculator.getMaxCrossAxisExtent(context, settingsProvider.libraryDensity); final crossAxisSpacing = GridLayoutConstants.crossAxisSpacing; diff --git a/lib/screens/livetv/tabs/guide_tab.dart b/lib/screens/livetv/tabs/guide_tab.dart index 5c1ce2fb8..c71f9baa3 100644 --- a/lib/screens/livetv/tabs/guide_tab.dart +++ b/lib/screens/livetv/tabs/guide_tab.dart @@ -244,7 +244,7 @@ class GuideTabState extends State { final offset = (minutesSinceStart / _minutesPerSlot) * _slotWidth; if (_gridHorizontalController.hasClients) { _gridHorizontalController.jumpTo( - (offset - MediaQuery.of(context).size.width / 3).clamp(0, _gridHorizontalController.position.maxScrollExtent), + (offset - MediaQuery.sizeOf(context).width / 3).clamp(0, _gridHorizontalController.position.maxScrollExtent), ); } }); diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index a8cb6a681..4c395e39e 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -1719,7 +1719,7 @@ class _MediaDetailScreenState extends State /// Uses the shared grid size calculator for consistency with library grids. double _getResponsiveCardWidth() { final density = context.read().libraryDensity; - final availableWidth = MediaQuery.of(context).size.width; + final availableWidth = MediaQuery.sizeOf(context).width; return GridSizeCalculator.getCellWidth(availableWidth, context, density); } @@ -2424,7 +2424,7 @@ class _MediaDetailScreenState extends State } // Determine header height based on screen size - final size = MediaQuery.of(context).size; + final size = MediaQuery.sizeOf(context); final headerHeight = size.height * 0.6; final content = OverlaySheetHost( diff --git a/lib/screens/profile/profile_switch_screen.dart b/lib/screens/profile/profile_switch_screen.dart index 92f327d9d..a152652ae 100644 --- a/lib/screens/profile/profile_switch_screen.dart +++ b/lib/screens/profile/profile_switch_screen.dart @@ -77,10 +77,10 @@ class _ProfileSwitchScreenState extends State { ), ) else if (users.isEmpty) - const SliverFillRemaining( + SliverFillRemaining( child: EmptyStateWidget( - message: 'No profiles available', - subtitle: 'Contact your Plex administrator to add profiles', + message: t.messages.noProfilesAvailable, + subtitle: t.messages.contactAdminForProfiles, icon: Symbols.person_off_rounded, ), ) diff --git a/lib/screens/profile/user_avatar_widget.dart b/lib/screens/profile/user_avatar_widget.dart index afdc32d8c..4e8295d85 100644 --- a/lib/screens/profile/user_avatar_widget.dart +++ b/lib/screens/profile/user_avatar_widget.dart @@ -12,7 +12,6 @@ class UserAvatarWidget extends StatelessWidget { final double size; final bool showIndicators; final bool useTextLabels; - final VoidCallback? onTap; const UserAvatarWidget({ super.key, @@ -20,7 +19,6 @@ class UserAvatarWidget extends StatelessWidget { this.size = 40, this.showIndicators = true, this.useTextLabels = false, - this.onTap, }); Widget _buildPlaceholderAvatar(ThemeData theme) { @@ -196,15 +194,13 @@ class UserAvatarWidget extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); - return useTextLabels - ? GestureDetector( - onTap: onTap, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [_buildAvatar(context, theme), ..._buildTextLabels(context, theme)], - ), - ) - : GestureDetector(onTap: onTap, child: _buildAvatar(context, theme)); + if (useTextLabels) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [_buildAvatar(context, theme), ..._buildTextLabels(context, theme)], + ); + } + return _buildAvatar(context, theme); } } diff --git a/lib/screens/settings/appearance_settings_screen.dart b/lib/screens/settings/appearance_settings_screen.dart index a4228122d..34538431f 100644 --- a/lib/screens/settings/appearance_settings_screen.dart +++ b/lib/screens/settings/appearance_settings_screen.dart @@ -142,8 +142,9 @@ class _AppearanceSettingsScreenState extends State { } Widget _buildDensitySelector() { - return Consumer( - builder: (context, settingsProvider, child) { + return Selector( + selector: (_, p) => p.libraryDensity, + builder: (context, density, _) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( @@ -156,11 +157,11 @@ class _AppearanceSettingsScreenState extends State { Text(t.settings.compact, style: const TextStyle(fontSize: 12, color: Colors.grey)), Expanded( child: FocusableSlider( - value: settingsProvider.libraryDensity.toDouble(), + value: density.toDouble(), min: 1, max: 5, divisions: 4, - onChanged: (value) => settingsProvider.setLibraryDensity(value.round()), + onChanged: (value) => context.read().setLibraryDensity(value.round()), ), ), Text(t.settings.comfortable, style: const TextStyle(fontSize: 12, color: Colors.grey)), @@ -174,8 +175,9 @@ class _AppearanceSettingsScreenState extends State { } Widget _buildViewModeSelector() { - return Consumer( - builder: (context, settingsProvider, child) { + return Selector( + selector: (_, p) => p.viewMode, + builder: (context, viewMode, _) { return SegmentedSetting( icon: Symbols.view_list_rounded, title: t.settings.viewMode, @@ -183,16 +185,17 @@ class _AppearanceSettingsScreenState extends State { ButtonSegment(value: settings.ViewMode.grid, label: Text(t.settings.gridView)), ButtonSegment(value: settings.ViewMode.list, label: Text(t.settings.listView)), ], - selected: settingsProvider.viewMode, - onChanged: (value) => settingsProvider.setViewMode(value), + selected: viewMode, + onChanged: (value) => context.read().setViewMode(value), ); }, ); } Widget _buildEpisodePosterModeSelector() { - return Consumer( - builder: (context, settingsProvider, child) { + return Selector( + selector: (_, p) => p.episodePosterMode, + builder: (context, mode, _) { return SegmentedSetting( icon: Symbols.image_rounded, title: t.settings.episodePosterMode, @@ -201,146 +204,112 @@ class _AppearanceSettingsScreenState extends State { ButtonSegment(value: settings.EpisodePosterMode.seasonPoster, label: Text(t.settings.seasonPoster)), ButtonSegment(value: settings.EpisodePosterMode.episodeThumbnail, label: Text(t.settings.episodeThumbnail)), ], - selected: settingsProvider.episodePosterMode, - onChanged: (value) => settingsProvider.setEpisodePosterMode(value), + selected: mode, + onChanged: (value) => context.read().setEpisodePosterMode(value), ); }, ); } + /// Shared scaffolding for the bool toggles on this screen. Each toggle + /// watches one `SettingsProvider` field via `Selector`, so flipping one + /// switch doesn't rebuild the others. + Widget _buildBoolToggle({ + required IconData icon, + required String title, + required String subtitle, + required bool Function(SettingsProvider) getter, + required Future Function(SettingsProvider, bool) setter, + }) { + return Selector( + selector: (_, p) => getter(p), + builder: (context, value, _) => SwitchListTile( + secondary: AppIcon(icon, fill: 1), + title: Text(title), + subtitle: Text(subtitle), + value: value, + onChanged: (v) => setter(context.read(), v), + ), + ); + } + // --- Home Screen section --- - Widget _buildShowHeroSection() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.featured_play_list_rounded, fill: 1), - title: Text(t.settings.showHeroSection), - subtitle: Text(t.settings.showHeroSectionDescription), - value: settingsProvider.showHeroSection, - onChanged: (value) async { - await settingsProvider.setShowHeroSection(value); - }, - ); - }, - ); - } + Widget _buildShowHeroSection() => _buildBoolToggle( + icon: Symbols.featured_play_list_rounded, + title: t.settings.showHeroSection, + subtitle: t.settings.showHeroSectionDescription, + getter: (p) => p.showHeroSection, + setter: (p, v) => p.setShowHeroSection(v), + ); - Widget _buildUseGlobalHubs() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.home_rounded, fill: 1), - title: Text(t.settings.useGlobalHubs), - subtitle: Text(t.settings.useGlobalHubsDescription), - value: settingsProvider.useGlobalHubs, - onChanged: (value) async { - await settingsProvider.setUseGlobalHubs(value); - }, - ); - }, - ); - } + Widget _buildUseGlobalHubs() => _buildBoolToggle( + icon: Symbols.home_rounded, + title: t.settings.useGlobalHubs, + subtitle: t.settings.useGlobalHubsDescription, + getter: (p) => p.useGlobalHubs, + setter: (p, v) => p.setUseGlobalHubs(v), + ); - Widget _buildShowServerNameOnHubs() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.dns_rounded, fill: 1), - title: Text(t.settings.showServerNameOnHubs), - subtitle: Text(t.settings.showServerNameOnHubsDescription), - value: settingsProvider.showServerNameOnHubs, - onChanged: (value) async { - await settingsProvider.setShowServerNameOnHubs(value); - }, - ); - }, - ); - } + Widget _buildShowServerNameOnHubs() => _buildBoolToggle( + icon: Symbols.dns_rounded, + title: t.settings.showServerNameOnHubs, + subtitle: t.settings.showServerNameOnHubsDescription, + getter: (p) => p.showServerNameOnHubs, + setter: (p, v) => p.setShowServerNameOnHubs(v), + ); // --- Navigation section --- - Widget _buildAlwaysKeepSidebarOpen() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.dock_to_left_rounded, fill: 1), - title: Text(t.settings.alwaysKeepSidebarOpen), - subtitle: Text(t.settings.alwaysKeepSidebarOpenDescription), - value: settingsProvider.alwaysKeepSidebarOpen, - onChanged: (value) async { - await settingsProvider.setAlwaysKeepSidebarOpen(value); - }, - ); - }, - ); - } + Widget _buildAlwaysKeepSidebarOpen() => _buildBoolToggle( + icon: Symbols.dock_to_left_rounded, + title: t.settings.alwaysKeepSidebarOpen, + subtitle: t.settings.alwaysKeepSidebarOpenDescription, + getter: (p) => p.alwaysKeepSidebarOpen, + setter: (p, v) => p.setAlwaysKeepSidebarOpen(v), + ); - Widget _buildShowNavBarLabels() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.label_rounded, fill: 1), - title: Text(t.settings.showNavBarLabels), - subtitle: Text(t.settings.showNavBarLabelsDescription), - value: settingsProvider.showNavBarLabels, - onChanged: (value) async { - await settingsProvider.setShowNavBarLabels(value); - }, - ); - }, - ); - } + Widget _buildShowNavBarLabels() => _buildBoolToggle( + icon: Symbols.label_rounded, + title: t.settings.showNavBarLabels, + subtitle: t.settings.showNavBarLabelsDescription, + getter: (p) => p.showNavBarLabels, + setter: (p, v) => p.setShowNavBarLabels(v), + ); - Widget _buildShowUnwatchedCount() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.counter_1_rounded, fill: 1), - title: Text(t.settings.showUnwatchedCount), - subtitle: Text(t.settings.showUnwatchedCountDescription), - value: settingsProvider.showUnwatchedCount, - onChanged: (value) async { - await settingsProvider.setShowUnwatchedCount(value); - }, - ); - }, - ); - } + Widget _buildShowUnwatchedCount() => _buildBoolToggle( + icon: Symbols.counter_1_rounded, + title: t.settings.showUnwatchedCount, + subtitle: t.settings.showUnwatchedCountDescription, + getter: (p) => p.showUnwatchedCount, + setter: (p, v) => p.setShowUnwatchedCount(v), + ); // --- Content section --- - Widget _buildLiveTvDefaultFavorites() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.star_rounded, fill: 1), - title: Text(t.settings.liveTvDefaultFavorites), - subtitle: Text(t.settings.liveTvDefaultFavoritesDescription), - value: settingsProvider.liveTvDefaultFavorites, - onChanged: (value) async { - await settingsProvider.setLiveTvDefaultFavorites(value); - }, - ); - }, - ); - } + Widget _buildLiveTvDefaultFavorites() => _buildBoolToggle( + icon: Symbols.star_rounded, + title: t.settings.liveTvDefaultFavorites, + subtitle: t.settings.liveTvDefaultFavoritesDescription, + getter: (p) => p.liveTvDefaultFavorites, + setter: (p, v) => p.setLiveTvDefaultFavorites(v), + ); - Widget _buildHideSpoilers() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.visibility_off_rounded, fill: 1), - title: Text(t.settings.hideSpoilers), - subtitle: Text(t.settings.hideSpoilersDescription), - value: settingsProvider.hideSpoilers, - onChanged: (value) async { - await settingsProvider.setHideSpoilers(value); - }, - ); - }, - ); - } + Widget _buildHideSpoilers() => _buildBoolToggle( + icon: Symbols.visibility_off_rounded, + title: t.settings.hideSpoilers, + subtitle: t.settings.hideSpoilersDescription, + getter: (p) => p.hideSpoilers, + setter: (p, v) => p.setHideSpoilers(v), + ); + + Widget _buildAutoHidePerformanceOverlay() => _buildBoolToggle( + icon: Symbols.speed_rounded, + title: t.settings.autoHidePerformanceOverlay, + subtitle: t.settings.autoHidePerformanceOverlayDescription, + getter: (p) => p.autoHidePerformanceOverlay, + setter: (p, v) => p.setAutoHidePerformanceOverlay(v), + ); Widget _buildRequireProfileSelection() { return Consumer( @@ -388,22 +357,6 @@ class _AppearanceSettingsScreenState extends State { ); } - Widget _buildAutoHidePerformanceOverlay() { - return Consumer( - builder: (context, settingsProvider, child) { - return SwitchListTile( - secondary: const AppIcon(Symbols.speed_rounded, fill: 1), - title: Text(t.settings.autoHidePerformanceOverlay), - subtitle: Text(t.settings.autoHidePerformanceOverlayDescription), - value: settingsProvider.autoHidePerformanceOverlay, - onChanged: (value) async { - await settingsProvider.setAutoHidePerformanceOverlay(value); - }, - ); - }, - ); - } - // --- Helpers --- String _getLanguageDisplayName(AppLocale locale) { diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index f031edb71..64e0496fe 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -1449,7 +1449,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin // them in a single prepare() — no media reload needed for selection. // MPV (all platforms including Android): external subs added after open via sub-add. await player!.open( - Media(result.videoUrl!, start: resumePosition, headers: plexHeaders, fps: preKnownFps), + Media(result.videoUrl!, start: resumePosition, headers: plexHeaders), play: !willAutoSwitch && (isExoPlayer || !hasExternalSubs), externalSubtitles: isExoPlayer && hasExternalSubs ? result.externalSubtitles : null, ); diff --git a/lib/services/download_manager_service.dart b/lib/services/download_manager_service.dart index 8681e9320..3d63ccca1 100644 --- a/lib/services/download_manager_service.dart +++ b/lib/services/download_manager_service.dart @@ -12,6 +12,7 @@ import 'saf_storage_service.dart'; import '../models/download_models.dart'; import '../models/plex_metadata.dart'; import '../models/plex_media_info.dart'; +import '../services/offline_mode_source.dart'; import '../services/plex_client.dart'; import '../services/download_storage_service.dart'; import '../services/plex_api_cache.dart'; @@ -71,6 +72,8 @@ class DownloadManagerService { PlexClient? Function(String serverId)? _clientResolver; PlexClient? _fallbackClient; + OfflineModeSource? _offlineSource; + // background_downloader state bool _fileDownloaderInitialized = false; static const _downloadGroup = 'video_downloads'; @@ -145,6 +148,14 @@ class DownloadManagerService { _clientResolver = resolver; } + /// Inject the offline-mode source. When `isOffline`, queue/resume paths skip + /// network work and defer until connectivity returns. + void setOfflineSource(OfflineModeSource? source) { + _offlineSource = source; + } + + bool get _isOffline => _offlineSource?.isOffline ?? false; + /// Look up the correct client for [serverId]. /// Returns null if the server is offline — callers should skip/defer the work. PlexClient? _getClient(String? serverId) { @@ -285,6 +296,11 @@ class DownloadManagerService { void resumeQueuedDownloads(PlexClient client) { _fallbackClient = client; + if (_isOffline) { + appLogger.d('Skipping resumeQueuedDownloads — offline'); + return; + } + // Attempt deferred supplementary downloads for recovered items _processPendingSupplementaryDownloads(client); @@ -349,13 +365,24 @@ class DownloadManagerService { } } + /// Cancel any per-download timers (progress debounce + auto-retry) for [key]. + /// Idempotent; safe to call from any terminal/pause path. + void _cancelDownloadTimers(String key) { + _progressDebounceTimers.remove(key)?.cancel(); + _autoRetryTimers.remove(key)?.cancel(); + } + /// Delete a file if it exists and log the deletion /// Returns true if file was deleted, false otherwise Future _deleteFileIfExists(File file, String description) async { - if (await file.exists()) { - await file.delete(); - appLogger.i('Deleted $description: ${file.path}'); - return true; + try { + if (await file.exists()) { + await file.delete(); + appLogger.i('Deleted $description: ${file.path}'); + return true; + } + } catch (e) { + appLogger.w('Failed to delete $description: ${file.path}', error: e); } return false; } @@ -715,6 +742,7 @@ class DownloadManagerService { /// Handle a system-initiated cancel — re-queue unless already completed. Future _onDownloadCanceled(String globalKey) async { + _cancelDownloadTimers(globalKey); final ctx = _pendingDownloadContext.remove(globalKey); if (ctx == null) return; if (_completingKeys.contains(globalKey)) return; @@ -737,6 +765,7 @@ class DownloadManagerService { appLogger.d('Ignoring failure event for $globalKey: completion in progress'); return; } + _cancelDownloadTimers(globalKey); _pendingDownloadContext.remove(globalKey); final existing = await _database.getDownloadedMedia(globalKey); @@ -767,7 +796,6 @@ class DownloadManagerService { ); await _transitionStatus(globalKey, DownloadStatus.failed, errorMessage: errorMessage); await _database.removeFromQueue(globalKey); - _autoRetryTimers[globalKey]?.cancel(); _autoRetryTimers[globalKey] = Timer(_autoRetryDelay, () { _autoRetryTimers.remove(globalKey); _performAutoRetry(globalKey); @@ -791,6 +819,7 @@ class DownloadManagerService { appLogger.d('Ignoring permanent failure event for $globalKey: completion in progress'); return; } + _cancelDownloadTimers(globalKey); _pendingDownloadContext.remove(globalKey); final existing = await _database.getDownloadedMedia(globalKey); @@ -840,8 +869,8 @@ class DownloadManagerService { } _completingKeys.add(globalKey); try { - // Flush any pending debounced progress write before completing - _progressDebounceTimers.remove(globalKey)?.cancel(); + // Flush any pending debounced progress write + cancel any scheduled retry + _cancelDownloadTimers(globalKey); // Fresh DB check — bail if already completed (guards against race with orphan scan) final existingCheck = await _database.getDownloadedMedia(globalKey); @@ -1242,6 +1271,7 @@ class DownloadManagerService { _pausingKeys.add(globalKey); try { + _cancelDownloadTimers(globalKey); final bgTaskId = await _database.getBgTaskId(globalKey); if (bgTaskId != null) { final task = await FileDownloader().taskForId(bgTaskId); @@ -1302,7 +1332,7 @@ class DownloadManagerService { /// Cancel a download Future cancelDownload(String globalKey) async { - _autoRetryTimers.remove(globalKey)?.cancel(); + _cancelDownloadTimers(globalKey); final bgTaskId = await _database.getBgTaskId(globalKey); if (bgTaskId != null) { await FileDownloader().cancelTaskWithId(bgTaskId); @@ -1314,7 +1344,7 @@ class DownloadManagerService { /// Delete a downloaded item and its files Future deleteDownload(String globalKey) async { - _autoRetryTimers.remove(globalKey)?.cancel(); + _cancelDownloadTimers(globalKey); // Cancel if actively downloading via background_downloader final bgTaskId = await _database.getBgTaskId(globalKey); if (bgTaskId != null) { @@ -1892,6 +1922,10 @@ class DownloadManagerService { timer.cancel(); } _autoRetryTimers.clear(); + _pendingDownloadContext.clear(); + _pendingSupplementaryDownloads.clear(); + _completingKeys.clear(); + _pausingKeys.clear(); _progressController.close(); _deletionProgressController.close(); } diff --git a/lib/services/download_storage_service.dart b/lib/services/download_storage_service.dart index c6d855a18..926371b00 100644 --- a/lib/services/download_storage_service.dart +++ b/lib/services/download_storage_service.dart @@ -9,6 +9,19 @@ import '../utils/app_logger.dart'; import '../utils/formatters.dart'; import 'settings_service.dart'; +/// Thrown when the downloads storage layer cannot create or access a directory +/// (permission denied, quota exceeded, SAF permission revoked, etc.). +class DownloadStorageException implements Exception { + final String message; + final String path; + final Object cause; + + DownloadStorageException(this.message, this.path, this.cause); + + @override + String toString() => 'DownloadStorageException: $message (path: $path, cause: $cause)'; +} + class DownloadStorageService { static DownloadStorageService? _instance; static DownloadStorageService get instance => _instance ??= DownloadStorageService._(); @@ -219,12 +232,17 @@ class DownloadStorageService { .trim(); } - /// Ensure a directory exists, creating it if necessary + /// Ensure a directory exists, creating it if necessary. + /// `Directory.create(recursive: true)` is idempotent — it no-ops if the + /// directory already exists. Future _ensureDirectoryExists(Directory dir) async { - if (!await dir.exists()) { + try { await dir.create(recursive: true); + return dir; + } catch (e, st) { + appLogger.e('Failed to ensure directory exists: ${dir.path}', error: e, stackTrace: st); + throw DownloadStorageException('Cannot create directory', dir.path, e); } - return dir; } /// Format a media title with optional year: "Title (YYYY)" or "Title" diff --git a/lib/services/sync_rule_executor.dart b/lib/services/sync_rule_executor.dart index 6818ab760..c0eebbdd5 100644 --- a/lib/services/sync_rule_executor.dart +++ b/lib/services/sync_rule_executor.dart @@ -5,9 +5,11 @@ import '../models/download_models.dart'; import '../models/plex_metadata.dart'; import '../utils/app_logger.dart'; import '../utils/content_utils.dart'; +import '../utils/episode_collection.dart'; import '../utils/global_key_utils.dart'; import 'download_manager_service.dart'; import 'multi_server_manager.dart'; +import 'offline_mode_source.dart'; import 'plex_client.dart'; /// Sync-rule filter values stored in `SyncRules.downloadFilter`. @@ -40,10 +42,18 @@ class SyncRuleExecutor { static const Duration _cooldownWifi = Duration(minutes: 30); static const Duration _cooldownCellular = Duration(hours: 3); + OfflineModeSource? _offlineSource; + SyncRuleExecutor({required AppDatabase database}) : _database = database; bool get isExecuting => _isExecuting; + /// Inject the offline-mode source so we can skip running rules when the + /// device has no Plex connectivity (every `getChildren` call would fail). + void setOfflineSource(OfflineModeSource? source) { + _offlineSource = source; + } + /// Execute every enabled sync rule. /// /// The adaptive cooldown (30 min on WiFi/Ethernet, 3 h on cellular) only @@ -67,6 +77,11 @@ class SyncRuleExecutor { return []; } + if (_offlineSource?.isOffline ?? false) { + appLogger.d('Skipping sync rules — offline'); + return []; + } + // Read connectivity once for both the WiFi-only gate and the cooldown pick. final List connectivity = await _readConnectivity(); if (await DownloadManagerService.shouldBlockDownloadOnCellularWith(connectivity)) { @@ -134,6 +149,11 @@ class SyncRuleExecutor { return null; } + if (_offlineSource?.isOffline ?? false) { + appLogger.d('Skipping single sync rule $globalKey — offline'); + return null; + } + if (await DownloadManagerService.shouldBlockDownloadOnCellular()) { appLogger.d('Skipping single sync rule $globalKey — cellular download blocked'); return null; @@ -210,9 +230,9 @@ class SyncRuleExecutor { }) async { final unwatchedEpisodes = []; if (rule.targetType == ContentTypes.show) { - await _collectEpisodesForShow(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes); + await collectEpisodesForShow(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes); } else { - await _collectEpisodesForSeason(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes); + await collectEpisodesForSeason(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes); } if (unwatchedEpisodes.isEmpty) { @@ -333,9 +353,9 @@ class SyncRuleExecutor { if (unwatchedOnly && item.isWatched && !item.hasActiveProgress) break; out.add(item); case ContentTypes.show: - await _collectEpisodesForShow(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out); + await collectEpisodesForShow(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out); case ContentTypes.season: - await _collectEpisodesForSeason(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out); + await collectEpisodesForSeason(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out); default: // Skip music, clips, nested collections/playlists, unknown types. break; @@ -343,34 +363,6 @@ class SyncRuleExecutor { } } - Future _collectEpisodesForShow( - PlexClient client, - String showRatingKey, { - required bool unwatchedOnly, - required List out, - }) async { - final seasons = await client.getChildren(showRatingKey); - for (final season in seasons) { - if (season.type == ContentTypes.season) { - await _collectEpisodesForSeason(client, season.ratingKey, unwatchedOnly: unwatchedOnly, out: out); - } - } - } - - Future _collectEpisodesForSeason( - PlexClient client, - String seasonRatingKey, { - required bool unwatchedOnly, - required List out, - }) async { - final episodes = await client.getChildren(seasonRatingKey); - for (final ep in episodes) { - if (ep.type != ContentTypes.episode) continue; - if (unwatchedOnly && ep.isWatched && !ep.hasActiveProgress) continue; - out.add(ep); - } - } - static bool _isActiveDownload(DownloadProgress? p) => p != null && (p.status == DownloadStatus.completed || diff --git a/lib/services/trakt/trakt_sync_queue.dart b/lib/services/trakt/trakt_sync_queue.dart index c893fb8b0..ee2a3843c 100644 --- a/lib/services/trakt/trakt_sync_queue.dart +++ b/lib/services/trakt/trakt_sync_queue.dart @@ -1,8 +1,10 @@ +import 'dart:async'; import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import '../../models/trakt/trakt_ids.dart'; +import '../../utils/app_logger.dart'; import 'trakt_constants.dart'; /// One pending watched/unwatched push waiting to be drained to Trakt. @@ -73,23 +75,43 @@ class TraktSyncQueueItem { /// /// Cap at [maxAttempts] before dropping permanently — matches /// `OfflineWatchSyncService.maxSyncAttempts`. +/// +/// Serialises all writes (`add`, `save`, `drainWith`) through a Completer chain +/// so concurrent `add()` calls don't interleave read-modify-write and lose items. class TraktSyncQueue { static const String _baseKey = 'trakt_sync_queue'; static const int maxAttempts = 5; + Future _writeLock = Future.value(); + + Future _locked(Future Function() action) { + final previous = _writeLock; + final completer = Completer(); + _writeLock = completer.future; + return previous.then((_) => action()).whenComplete(completer.complete); + } + Future> load(String userUuid) async { final prefs = await SharedPreferences.getInstance(); - final raw = prefs.getString(traktUserKey(userUuid, _baseKey)); + final key = traktUserKey(userUuid, _baseKey); + final raw = prefs.getString(key); if (raw == null) return []; try { final list = json.decode(raw) as List; return list.map((e) => TraktSyncQueueItem.fromJson(e as Map)).toList(); - } catch (_) { + } catch (e, st) { + appLogger.e('Trakt sync queue parse failed, discarding', error: e, stackTrace: st); + await prefs.setString(traktUserKey(userUuid, '${_baseKey}_corrupt'), raw); + await prefs.remove(key); return []; } } - Future save(String userUuid, List items) async { + Future save(String userUuid, List items) { + return _locked(() => _saveRaw(userUuid, items)); + } + + Future _saveRaw(String userUuid, List items) async { final prefs = await SharedPreferences.getInstance(); final key = traktUserKey(userUuid, _baseKey); if (items.isEmpty) { @@ -99,9 +121,30 @@ class TraktSyncQueue { } } - Future add(String userUuid, TraktSyncQueueItem item) async { - final items = await load(userUuid); - items.add(item); - await save(userUuid, items); + Future add(String userUuid, TraktSyncQueueItem item) { + return _locked(() async { + final items = await load(userUuid); + items.add(item); + await _saveRaw(userUuid, items); + }); + } + + /// Atomic drain: load the queue, run [processor] for each item, and save the + /// items the processor decided to retain. Holds the write lock for the whole + /// cycle so concurrent `add()`s wait until the drain completes (no lost items). + /// + /// [processor] returns `null` to drop the item, or a (possibly mutated) item + /// to retain for the next drain (e.g. `item.incrementAttempts()`). + Future drainWith(String userUuid, Future Function(TraktSyncQueueItem) processor) { + return _locked(() async { + final items = await load(userUuid); + if (items.isEmpty) return; + final remaining = []; + for (final item in items) { + final keep = await processor(item); + if (keep != null) remaining.add(keep); + } + await _saveRaw(userUuid, remaining); + }); } } diff --git a/lib/services/trakt/trakt_sync_service.dart b/lib/services/trakt/trakt_sync_service.dart index 1911bf37e..ce23dca20 100644 --- a/lib/services/trakt/trakt_sync_service.dart +++ b/lib/services/trakt/trakt_sync_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import '../../models/trakt/trakt_ids.dart'; import '../../models/trakt/trakt_scrobble_request.dart'; @@ -40,6 +41,12 @@ class TraktSyncService { /// key GUID cache survives a binge-watch session. final Map _resolvers = {}; + /// Fallback buffer for items that failed to persist to the on-disk queue + /// (e.g. SharedPreferences write threw). Retried on next `flushQueue`. + /// Bounded to keep memory pressure finite; oldest items drop first. + static const int _maxInMemoryFallback = 100; + final Queue _inMemoryFallback = Queue(); + bool _isFlushing = false; Future initialize({required MultiServerManager serverManager}) async { @@ -50,7 +57,11 @@ class TraktSyncService { final settings = await SettingsService.getInstance(); _isEnabled = settings.getEnableTraktWatchedSync(); - _subscription = WatchStateNotifier().stream.listen(_onWatchStateEvent); + _subscription = WatchStateNotifier().stream.listen( + _onWatchStateEvent, + onError: (Object e, StackTrace st) => + appLogger.w('Trakt sync: watch event handler error', error: e, stackTrace: st), + ); } Future setEnabled(bool enabled) async { @@ -166,7 +177,7 @@ class TraktSyncService { Future _trySendOrQueue(TraktSyncQueueItem item, TraktScrobbleRequest body) async { final client = _client; if (client == null) { - await _queue.add(_activeUserUuid, item); + await _persistOrBuffer(item); return; } try { @@ -174,7 +185,27 @@ class TraktSyncService { appLogger.d('Trakt sync: ${item.op.name} ${item.ratingKey} → ok'); } catch (e) { appLogger.d('Trakt sync: ${item.op.name} ${item.ratingKey} failed, queuing', error: e); + await _persistOrBuffer(item); + } + } + + /// Persist an item to the on-disk queue; fall back to a bounded in-memory + /// buffer if the disk write throws (e.g. disk full, SAF permission revoked). + /// Retried at the start of the next `flushQueue` run. + Future _persistOrBuffer(TraktSyncQueueItem item) async { + try { await _queue.add(_activeUserUuid, item); + } catch (e, st) { + appLogger.e( + 'Trakt sync: queue persist failed for ${item.op.name} ${item.ratingKey}, buffering in memory', + error: e, + stackTrace: st, + ); + if (_inMemoryFallback.length >= _maxInMemoryFallback) { + final dropped = _inMemoryFallback.removeFirst(); + appLogger.w('Trakt sync: in-memory fallback full, dropping ${dropped.op.name} ${dropped.ratingKey}'); + } + _inMemoryFallback.addLast(item); } } @@ -193,31 +224,41 @@ class TraktSyncService { if (client == null) return; _isFlushing = true; try { - final items = await _queue.load(_activeUserUuid); - if (items.isEmpty) return; + await _recoverInMemoryFallback(); - final remaining = []; - for (final item in items) { + await _queue.drainWith(_activeUserUuid, (item) async { if (item.attempts >= TraktSyncQueue.maxAttempts) { appLogger.w('Trakt sync: dropping ${item.op.name} ${item.ratingKey} after ${item.attempts} attempts'); - continue; + return null; } try { await _dispatch(client, item, _bodyFor(item)); appLogger.d('Trakt sync: drained ${item.op.name} ${item.ratingKey}'); + await Future.delayed(_queueRequestSpacing); + return null; } catch (e) { appLogger.d('Trakt sync: drain failed for ${item.ratingKey}, will retry', error: e); - remaining.add(item.incrementAttempts()); + await Future.delayed(_queueRequestSpacing); + return item.incrementAttempts(); } - await Future.delayed(_queueRequestSpacing); - } - - await _queue.save(_activeUserUuid, remaining); + }); } finally { _isFlushing = false; } } + /// Try to move items buffered in memory (because prior disk writes failed) + /// back onto the persistent queue. Best-effort; items that still can't be + /// persisted stay in the buffer for the next flush. + Future _recoverInMemoryFallback() async { + if (_inMemoryFallback.isEmpty) return; + final snapshot = List.from(_inMemoryFallback); + _inMemoryFallback.clear(); + for (final item in snapshot) { + await _persistOrBuffer(item); + } + } + TraktScrobbleRequest _bodyFor(TraktSyncQueueItem item) { return switch (item.kind) { TraktMediaKind.movie => TraktScrobbleRequest.movie(ids: item.ids), diff --git a/lib/utils/content_utils.dart b/lib/utils/content_utils.dart index 88193b6b8..ec8b76fd6 100644 --- a/lib/utils/content_utils.dart +++ b/lib/utils/content_utils.dart @@ -89,12 +89,7 @@ extension PlexMetadataType on PlexMetadata { bool get isMovie => _lowerType == ContentTypes.movie; bool get isSeason => _lowerType == ContentTypes.season; bool get isEpisode => _lowerType == ContentTypes.episode; - bool get isArtist => _lowerType == ContentTypes.artist; - bool get isAlbum => _lowerType == ContentTypes.album; - bool get isTrack => _lowerType == ContentTypes.track; bool get isCollection => _lowerType == ContentTypes.collection; - bool get isPlaylist => _lowerType == ContentTypes.playlist; - bool get isClip => _lowerType == ContentTypes.clip; bool get isMusicContent => ContentTypes.musicTypes.contains(_lowerType); bool get isVideoContent => ContentTypes.videoTypes.contains(_lowerType); diff --git a/lib/utils/episode_collection.dart b/lib/utils/episode_collection.dart new file mode 100644 index 000000000..c361bfc61 --- /dev/null +++ b/lib/utils/episode_collection.dart @@ -0,0 +1,52 @@ +import '../models/plex_metadata.dart'; +import '../services/plex_client.dart'; +import '../utils/app_logger.dart'; +import '../utils/content_utils.dart'; + +/// Walk the children of a show and collect every episode into [out]. +/// +/// - Per-season fetch failures are logged and skipped (one bad season doesn't +/// discard progress from the others). +/// - A failure to fetch the show's own children is logged and leaves [out] +/// empty. +/// - [unwatchedOnly] skips episodes that are watched and have no active +/// progress. +Future collectEpisodesForShow( + PlexClient client, + String showRatingKey, { + required bool unwatchedOnly, + required List out, +}) async { + final List seasons; + try { + seasons = await client.getChildren(showRatingKey); + } catch (e) { + appLogger.w('Episode collection: show $showRatingKey getChildren failed, skipping', error: e); + return; + } + for (final season in seasons) { + if (season.type != ContentTypes.season) continue; + try { + await collectEpisodesForSeason(client, season.ratingKey, unwatchedOnly: unwatchedOnly, out: out); + } catch (e) { + appLogger.w('Episode collection: season ${season.ratingKey} fetch failed, skipping', error: e); + } + } +} + +/// Fetch the episodes of a season and append the ones passing [unwatchedOnly] +/// to [out]. Throws if the underlying `getChildren` fails — callers that want +/// per-season resilience should wrap in try/catch (see [collectEpisodesForShow]). +Future collectEpisodesForSeason( + PlexClient client, + String seasonRatingKey, { + required bool unwatchedOnly, + required List out, +}) async { + final episodes = await client.getChildren(seasonRatingKey); + for (final ep in episodes) { + if (ep.type != ContentTypes.episode) continue; + if (unwatchedOnly && ep.isWatched && !ep.hasActiveProgress) continue; + out.add(ep); + } +} diff --git a/lib/utils/plex_cache_parser.dart b/lib/utils/plex_cache_parser.dart index 3a9a3e167..08ef46ba6 100644 --- a/lib/utils/plex_cache_parser.dart +++ b/lib/utils/plex_cache_parser.dart @@ -21,35 +21,10 @@ class PlexCacheParser { return list.first as Map; } - /// Check if a cached response has valid metadata - static bool hasMetadata(Map? cached) { - final list = extractMetadataList(cached); - return list != null && list.isNotEmpty; - } - - /// Extract Directory list from a cached response (for libraries, playlists) - static List? extractDirectoryList(Map? cached) { - if (cached == null) return null; - return cached['MediaContainer']?['Directory'] as List?; - } - - /// Extract Hub list from a cached response - static List? extractHubList(Map? cached) { - if (cached == null) return null; - return cached['MediaContainer']?['Hub'] as List?; - } - /// Extract Chapter list from the first metadata item static List? extractChapters(Map? cached) { final metadata = extractFirstMetadata(cached); if (metadata == null) return null; return metadata['Chapter'] as List?; } - - /// Extract Marker list from the first metadata item - static List? extractMarkers(Map? cached) { - final metadata = extractFirstMetadata(cached); - if (metadata == null) return null; - return metadata['Marker'] as List?; - } } diff --git a/lib/utils/plex_image_helper.dart b/lib/utils/plex_image_helper.dart index 0d908e6e1..044128072 100644 --- a/lib/utils/plex_image_helper.dart +++ b/lib/utils/plex_image_helper.dart @@ -243,15 +243,4 @@ class PlexImageHelper { return true; } - - /// Creates a consistent cache key for rounded dimensions - static String generateCacheKey({ - required String originalPath, - required int width, - required int height, - String? serverId, - }) { - final serverPrefix = serverId != null ? '${serverId}_' : ''; - return '${serverPrefix}transcode_${width}x${height}_${originalPath.hashCode}'; - } } diff --git a/lib/widgets/download_status_icon.dart b/lib/widgets/download_status_icon.dart new file mode 100644 index 000000000..0322f9e97 --- /dev/null +++ b/lib/widgets/download_status_icon.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; + +import '../models/download_models.dart'; +import '../widgets/app_icon.dart'; + +/// Visual weight preset. +/// +/// - [muted] blends the status color with the surrounding muted text color +/// (used in compact list contexts like episode rows where a saturated color +/// would fight the primary content). +/// - [saturated] uses the status color directly (used in tree view / expanded +/// contexts where the status is the primary signal). +enum DownloadStatusIconVariant { muted, saturated } + +/// Renders a compact status indicator for a download: queued/paused/failed/etc. +/// When [status] is [DownloadStatus.downloading] and [progress] is non-null, +/// renders a dual-ring progress indicator instead of a static icon. +/// +/// Returns `SizedBox.shrink()` for any status that shouldn't have a visible +/// indicator in the caller's context (e.g. partial). +class DownloadStatusIcon extends StatelessWidget { + final DownloadStatus? status; + final double size; + final DownloadStatusIconVariant variant; + + /// Optional 0.0–1.0 progress for the downloading state's ring. + /// If null while downloading, shows an indeterminate spinner. + final double? progress; + + /// Color to blend with in [DownloadStatusIconVariant.muted]. Required when + /// variant=muted (usually `tokens(context).textMuted`). + final Color? mutedBase; + + /// Optional override for the primary color (e.g. used in downloading + /// variant=muted to pick the theme's primary rather than a fixed blue). + final Color? overrideColor; + + const DownloadStatusIcon({ + super.key, + required this.status, + this.size = 16, + this.variant = DownloadStatusIconVariant.saturated, + this.progress, + this.mutedBase, + this.overrideColor, + }); + + Color _tint(Color base) { + if (variant == DownloadStatusIconVariant.saturated || mutedBase == null) return base; + return Color.lerp(mutedBase, base, 0.3) ?? base; + } + + @override + Widget build(BuildContext context) { + final s = status; + if (s == null) return const SizedBox.shrink(); + + switch (s) { + case DownloadStatus.queued: + return AppIcon(Symbols.schedule_rounded, fill: 1, size: size, color: _tint(Colors.orange)); + case DownloadStatus.downloading: + // No progress value — render a static "downloading" icon (callers + // without per-item progress, e.g. the download tree view). + if (progress == null) { + return AppIcon(Symbols.downloading_rounded, fill: 1, size: size, color: _tint(overrideColor ?? Colors.blue)); + } + final primary = overrideColor ?? Theme.of(context).colorScheme.primary; + final tinted = _tint(primary); + return SizedBox( + width: size, + height: size, + child: Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator( + value: 1.0, + strokeWidth: size * 0.1, + valueColor: AlwaysStoppedAnimation(tinted.withValues(alpha: 0.3)), + ), + CircularProgressIndicator( + value: progress, + strokeWidth: size * 0.1, + valueColor: AlwaysStoppedAnimation(tinted), + ), + ], + ), + ); + case DownloadStatus.paused: + return AppIcon( + Symbols.pause_circle_outline_rounded, + fill: 1, + size: size, + color: _tint(variant == DownloadStatusIconVariant.muted ? Colors.amber : Colors.grey), + ); + case DownloadStatus.failed: + return AppIcon( + variant == DownloadStatusIconVariant.muted ? Symbols.error_outline_rounded : Symbols.error_rounded, + fill: 1, + size: size, + color: _tint(Colors.red), + ); + case DownloadStatus.cancelled: + return AppIcon(Symbols.cancel_rounded, fill: 1, size: size, color: _tint(Colors.grey)); + case DownloadStatus.completed: + return AppIcon( + variant == DownloadStatusIconVariant.muted + ? Symbols.file_download_done_rounded + : Symbols.check_circle_rounded, + fill: 1, + size: size, + color: _tint(Colors.green), + ); + case DownloadStatus.partial: + return AppIcon(Symbols.downloading_rounded, fill: 1, size: size, color: _tint(Colors.orange)); + } + } +} + +/// Indeterminate spinner used while the download is being queued (pre-status). +/// Separate widget because it doesn't correspond to a [DownloadStatus] value. +class DownloadQueueingSpinner extends StatelessWidget { + final double size; + final Color? color; + + const DownloadQueueingSpinner({super.key, this.size = 12, this.color}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: size, + height: size, + child: CircularProgressIndicator(strokeWidth: size * 0.125, color: color), + ); + } +} diff --git a/lib/widgets/download_tree_view.dart b/lib/widgets/download_tree_view.dart index fd04ba9f5..47f713340 100644 --- a/lib/widgets/download_tree_view.dart +++ b/lib/widgets/download_tree_view.dart @@ -7,6 +7,7 @@ import '../models/download_models.dart'; import '../models/plex_metadata.dart'; import '../utils/content_utils.dart'; import '../utils/dialogs.dart'; +import 'download_status_icon.dart'; /// Represents a node in the download tree class DownloadTreeNode { @@ -707,41 +708,7 @@ class _DownloadTreeItemState extends State<_DownloadTreeItem> { } Widget _buildStatusIcon(DownloadStatus status) { - IconData iconData; - Color? color; - - switch (status) { - case DownloadStatus.downloading: - iconData = Symbols.downloading_rounded; - color = Colors.blue; - break; - case DownloadStatus.queued: - iconData = Symbols.schedule_rounded; - color = Colors.orange; - break; - case DownloadStatus.paused: - iconData = Symbols.pause_circle_outline_rounded; - color = Colors.grey; - break; - case DownloadStatus.completed: - iconData = Symbols.check_circle_rounded; - color = Colors.green; - break; - case DownloadStatus.failed: - iconData = Symbols.error_rounded; - color = Colors.red; - break; - case DownloadStatus.cancelled: - iconData = Symbols.cancel_rounded; - color = Colors.grey; - break; - case DownloadStatus.partial: - iconData = Symbols.downloading_rounded; - color = Colors.orange; - break; - } - - return AppIcon(iconData, fill: 1, size: 20, color: color); + return DownloadStatusIcon(status: status, size: 20); } String _getNodeSummary() { diff --git a/lib/widgets/episode_card.dart b/lib/widgets/episode_card.dart index e8a0c2558..10b6a96f9 100644 --- a/lib/widgets/episode_card.dart +++ b/lib/widgets/episode_card.dart @@ -11,6 +11,7 @@ import '../providers/download_provider.dart'; import '../providers/settings_provider.dart'; import '../utils/content_utils.dart'; import '../widgets/collapsible_text.dart'; +import '../widgets/download_status_icon.dart'; import '../widgets/plex_optimized_image.dart'; import '../models/plex_metadata.dart'; import '../utils/platform_detector.dart'; @@ -238,99 +239,30 @@ class _EpisodeCardState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Episode number and title with download status - Consumer( - builder: (context, downloadProvider, _) { + Selector( + selector: (_, p) => _DownloadSlice.from( + p.getProgress(widget.episode.globalKey), + p.isQueueing(widget.episode.globalKey), + ), + builder: (context, slice, _) { // Build download status icon based on state Widget? downloadStatusIcon; // Only show download status in online mode if (!widget.isOffline && widget.episode.serverId != null) { - final globalKey = widget.episode.globalKey; - final progress = downloadProvider.getProgress(globalKey); - final isQueueing = downloadProvider.isQueueing(globalKey); + final status = slice.status; + final mutedBase = tokens(context).textMuted; - // Helper to get status-specific muted color - Color getMutedColor(Color baseColor) { - return Color.lerp( - tokens(context).textMuted, - baseColor, - 0.3, // 30% of the status color, 70% muted - )!; - } - - if (isQueueing) { - // Queueing state - building queue - downloadStatusIcon = SizedBox( - width: 12, - height: 12, - child: CircularProgressIndicator(strokeWidth: 1.5, color: tokens(context).textMuted), - ); - } else if (progress?.status == DownloadStatus.queued) { - // Queued state - waiting to download - downloadStatusIcon = AppIcon( - Symbols.schedule_rounded, - fill: 1, - size: 12, - color: getMutedColor(Colors.orange), - ); - } else if (progress?.status == DownloadStatus.downloading) { - // Downloading state - active download with radial progress - downloadStatusIcon = SizedBox( - width: 14, - height: 14, - child: Stack( - alignment: Alignment.center, - children: [ - // Background circle - CircularProgressIndicator( - value: 1.0, - strokeWidth: 1.5, - valueColor: AlwaysStoppedAnimation( - getMutedColor(Theme.of(context).colorScheme.primary).withValues(alpha: 0.3), - ), - ), - // Progress circle - CircularProgressIndicator( - value: progress?.progressPercent, - strokeWidth: 1.5, - valueColor: AlwaysStoppedAnimation( - getMutedColor(Theme.of(context).colorScheme.primary), - ), - ), - ], - ), - ); - } else if (progress?.status == DownloadStatus.paused) { - // Paused state - download paused - downloadStatusIcon = AppIcon( - Symbols.pause_circle_outline_rounded, - fill: 1, - size: 12, - color: getMutedColor(Colors.amber), - ); - } else if (progress?.status == DownloadStatus.failed) { - // Failed state - download failed - downloadStatusIcon = AppIcon( - Symbols.error_outline_rounded, - fill: 1, - size: 12, - color: getMutedColor(Colors.red), - ); - } else if (progress?.status == DownloadStatus.cancelled) { - // Cancelled state - download cancelled - downloadStatusIcon = AppIcon( - Symbols.cancel_rounded, - fill: 1, - size: 12, - color: getMutedColor(Colors.grey), - ); - } else if (progress?.status == DownloadStatus.completed) { - // Completed state - download complete - downloadStatusIcon = AppIcon( - Symbols.file_download_done_rounded, - fill: 1, - size: 12, - color: getMutedColor(Colors.green), + if (slice.isQueueing) { + downloadStatusIcon = DownloadQueueingSpinner(size: 12, color: mutedBase); + } else if (status != null) { + final iconSize = status == DownloadStatus.downloading ? 14.0 : 12.0; + downloadStatusIcon = DownloadStatusIcon( + status: status, + size: iconSize, + variant: DownloadStatusIconVariant.muted, + mutedBase: mutedBase, + progress: slice.progressPercent, ); } // Note: No icon shown if not downloaded (null) @@ -437,3 +369,28 @@ class _EpisodeCardState extends State { return const PlaceholderContainer(child: AppIcon(Symbols.movie_rounded, fill: 1, size: 32)); } } + +/// Captures only primitives so Selector equality avoids rebuilds on unrelated +/// download ticks (e.g. other episodes, unused `DownloadProgress` fields). +class _DownloadSlice { + final DownloadStatus? status; + final double? progressPercent; + final bool isQueueing; + + const _DownloadSlice({required this.status, required this.progressPercent, required this.isQueueing}); + + factory _DownloadSlice.from(DownloadProgress? p, bool isQueueing) => + _DownloadSlice(status: p?.status, progressPercent: p?.progressPercent, isQueueing: isQueueing); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is _DownloadSlice && + other.status == status && + other.progressPercent == progressPercent && + other.isQueueing == isQueueing; + } + + @override + int get hashCode => Object.hash(status, progressPercent, isQueueing); +} diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index c56ff1de7..f8ee89a65 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -881,7 +881,7 @@ class MediaContextMenuState extends State { if (sectionId == null) { if (context.mounted) { - showErrorSnackBar(context, 'Unable to determine library section for this item'); + showErrorSnackBar(context, t.messages.unableToDetermineLibrarySection); } return; } @@ -1514,7 +1514,9 @@ class _CollectionSelectionDialogState extends State<_CollectionSelectionDialog> return ListTile( leading: const AppIcon(Symbols.collections_rounded, fill: 1), title: Text(collection.title!), - subtitle: collection.childCount != null ? Text('${collection.childCount} items') : null, + subtitle: collection.childCount != null + ? Text(t.playlists.itemCount(count: collection.childCount!)) + : null, onTap: () => Navigator.pop(context, collection.ratingKey), ); },