···2323import 'package:spark/src/core/utils/logging/log_service.dart';
2424import 'package:spark/src/core/utils/logging/logger.dart';
2525import 'package:spark/src/core/utils/share_urls.dart';
2626+import 'package:spark/src/core/utils/video_upload_exception.dart';
26272728/// Implementation of Feed-related API endpoints
2829class FeedRepositoryImpl implements FeedRepository {
···11941195 }
1195119611961197 // Check if the video is in a compatible format
11971197- final videoBytes = await file.readAsBytes();
11981198- if (videoBytes.isEmpty) {
11981198+ final videoSizeBytes = await file.length();
11991199+ if (videoSizeBytes == 0) {
11991200 throw Exception('Video file is empty');
12001201 }
1201120212021202- _logger.i('Video file size: ${videoBytes.length} bytes');
12031203+ _logger.i('Video file size: $videoSizeBytes bytes');
12041204+ final maxUploadSizeBytes = (AppConfig.maxUploadSizeMB * 1024 * 1024)
12051205+ .round();
12061206+ if (maxUploadSizeBytes > 0 && videoSizeBytes > maxUploadSizeBytes) {
12071207+ _logger.w(
12081208+ 'Video file exceeds upload limit: $videoSizeBytes bytes '
12091209+ '(limit: $maxUploadSizeBytes bytes)',
12101210+ );
12111211+ throw VideoUploadException(
12121212+ 'Video is too large to upload.',
12131213+ statusCode: 413,
12141214+ uploadSizeBytes: videoSizeBytes,
12151215+ limitBytes: maxUploadSizeBytes,
12161216+ );
12171217+ }
12181218+ final videoBytes = await file.readAsBytes();
1203121912041220 final pdsService = authAtProto.service;
12051221 final serviceTokenRes = await authAtProto.server.getServiceAuth(
···12261242 );
1227124312281244 if (response.statusCode != 200) {
12291229- throw Exception(
12301230- 'Failed to upload video: ${response.statusCode} ${response.body}',
12451245+ _logger.e(
12461246+ 'Video upload failed: ${response.statusCode} ${response.body}',
12471247+ );
12481248+ throw VideoUploadException(
12491249+ response.statusCode == 413
12501250+ ? 'Video is too large to upload.'
12511251+ : 'Failed to upload video.',
12521252+ statusCode: response.statusCode,
12531253+ uploadSizeBytes: videoSizeBytes,
12541254+ limitBytes: maxUploadSizeBytes > 0 ? maxUploadSizeBytes : null,
12551255+ responseBody: response.body,
12311256 );
12321257 }
12331258
+39
lib/src/core/utils/error_messages.dart
···11+import 'package:spark/src/core/utils/video_upload_exception.dart';
22+13/// Utility for converting exceptions into user-friendly error messages.
24/// This prevents exposing internal implementation details to users while still
35/// providing helpful feedback.
···810 return 'An unexpected error occurred';
911 }
10121313+ if (error is VideoUploadException) {
1414+ if (error.isPayloadTooLarge) {
1515+ final uploadSize = error.uploadSizeBytes;
1616+ final limit = error.limitBytes;
1717+ if (uploadSize != null && limit != null) {
1818+ return 'This video is too large to upload '
1919+ '(${_formatBytes(uploadSize)}). Please trim or compress it under '
2020+ '${_formatBytes(limit)} and try again.';
2121+ }
2222+ return 'This video is too large to upload. Please trim or compress it and try again.';
2323+ }
2424+ return 'Unable to upload video. Please try again';
2525+ }
2626+1127 final errorStr = error.toString().toLowerCase();
2828+2929+ // Upload size errors
3030+ if (errorStr.contains('413') ||
3131+ errorStr.contains('payload too large') ||
3232+ errorStr.contains('too large')) {
3333+ return 'This file is too large to upload. Please trim or compress it and try again.';
3434+ }
12351336 // Network errors
1437 if (errorStr.contains('socketexception') ||
···111134 }
112135113136 return baseMessage;
137137+ }
138138+139139+ static String _formatBytes(int bytes) {
140140+ const mb = 1024 * 1024;
141141+ if (bytes >= mb) {
142142+ final value = bytes / mb;
143143+ final formatted = value >= 10
144144+ ? value.toStringAsFixed(0)
145145+ : value.toStringAsFixed(1);
146146+ return '$formatted MB';
147147+ }
148148+ const kb = 1024;
149149+ if (bytes >= kb) {
150150+ return '${(bytes / kb).toStringAsFixed(0)} KB';
151151+ }
152152+ return '$bytes B';
114153 }
115154}
+25
lib/src/core/utils/video_upload_exception.dart
···11+/// Error raised when the video processing service rejects an upload.
22+class VideoUploadException implements Exception {
33+ const VideoUploadException(
44+ this.message, {
55+ this.statusCode,
66+ this.uploadSizeBytes,
77+ this.limitBytes,
88+ this.responseBody,
99+ });
1010+1111+ final String message;
1212+ final int? statusCode;
1313+ final int? uploadSizeBytes;
1414+ final int? limitBytes;
1515+ final String? responseBody;
1616+1717+ bool get isPayloadTooLarge =>
1818+ statusCode == 413 ||
1919+ (uploadSizeBytes != null &&
2020+ limitBytes != null &&
2121+ uploadSizeBytes! > limitBytes!);
2222+2323+ @override
2424+ String toString() => message;
2525+}