// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member part of 'track_metadata_screen.dart'; // Display helpers: quality/bitrate labels, service track-id formatting, // snackbars, and path formatting. extension _TrackMetadataDisplay on _TrackMetadataScreenState { bool _isBitrateFormatValue(String? value) { return const { 'aac', 'eac3', 'ac3', 'ac4', 'mp3', 'opus', 'm4a', }.contains(normalizeAudioFormatValue(value)); } String? _usableStoredQuality(String? quality) { final normalized = normalizeOptionalString(quality); if (normalized == null || isPlaceholderQualityLabel(normalized)) { return null; } final bitrateMatch = RegExp( r'\b(\d+)\s*kbps\b', caseSensitive: false, ).firstMatch(normalized); if (bitrateMatch != null) { final bitrate = int.tryParse(bitrateMatch.group(1) ?? ''); if (bitrate != null && bitrate < 16) return null; } return normalized; } String? _displayQualityForValues({ required String? format, int? bitDepth, int? sampleRate, int? bitrateKbps, String? storedQuality, }) { final normalizedFormat = normalizeAudioFormatValue(format); final formatLabel = normalizedFormat == null ? normalizeOptionalString(format)?.toUpperCase() : _formatLabelForRaw(normalizedFormat); if (_isBitrateFormatValue(normalizedFormat)) { return buildDisplayAudioQuality( bitrateKbps: bitrateKbps, format: formatLabel, ) ?? _usableStoredQuality(storedQuality) ?? formatLabel; } return buildDisplayAudioQuality( bitDepth: bitDepth, sampleRate: sampleRate, storedQuality: _usableStoredQuality(storedQuality), ); } String _displayServiceTrackId(String value) { final raw = value.trim(); if (raw.isEmpty) return raw; final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$'); if (raw.startsWith('deezer:')) return raw.substring('deezer:'.length); if (raw.startsWith('tidal:')) return raw.substring('tidal:'.length); if (raw.startsWith('qobuz:')) return raw.substring('qobuz:'.length); if (spotifyTrackIdPattern.hasMatch(raw)) return raw; if (raw.startsWith('spotify:')) { final last = raw.split(':').last.trim(); if (spotifyTrackIdPattern.hasMatch(last)) return last; return raw; } final uri = Uri.tryParse(raw); if (uri != null && uri.host.contains('spotify.com') && uri.pathSegments.length >= 2 && uri.pathSegments.first == 'track') { final candidate = uri.pathSegments[1].trim(); if (spotifyTrackIdPattern.hasMatch(candidate)) { return candidate; } } return raw; } String _serviceForTrackId(String value, {required String fallbackService}) { final raw = value.trim(); if (raw.isEmpty) return fallbackService; final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$'); if (raw.startsWith('deezer:')) return MusicServices.deezer; if (raw.startsWith('tidal:')) return MusicServices.tidal; if (raw.startsWith('qobuz:')) return MusicServices.qobuz; if (raw.startsWith('spotify:')) return MusicServices.spotify; if (spotifyTrackIdPattern.hasMatch(raw)) return MusicServices.spotify; final uri = Uri.tryParse(raw); if (uri != null) { final host = uri.host.toLowerCase(); if (host.contains('spotify.com')) return MusicServices.spotify; if (host.contains('deezer.com')) return MusicServices.deezer; if (host.contains('tidal.com')) return MusicServices.tidal; if (host.contains('qobuz.com')) return MusicServices.qobuz; } return fallbackService; } String? get _displayAudioQuality { final fileName = _extractFileNameFromPathOrUri(cleanFilePath); final fileExt = fileName.contains('.') ? fileName.split('.').last.toUpperCase() : null; return _displayQualityForValues( format: _storedAudioFormat ?? fileExt, bitDepth: bitDepth, sampleRate: sampleRate, bitrateKbps: _audioBitrate, storedQuality: _quality, ); } /// The raw file path, with EXISTS: prefix stripped but #trackNN preserved. /// Use this when you need the full virtual path (e.g. for display or DB lookups). String get rawFilePath { final path = _filePath; return path.startsWith('EXISTS:') ? path.substring(7) : path; } /// The clean file path with both EXISTS: prefix and #trackNN suffix stripped. /// Use this for actual filesystem/SAF operations. String get cleanFilePath { var path = _filePath; if (path.startsWith('EXISTS:')) path = path.substring(7); if (isCueVirtualPath(path)) path = stripCueTrackSuffix(path); return path; } bool get _isCueVirtualTrack => isCueVirtualPath(rawFilePath); String _cueVirtualTrackGuidance(BuildContext context) { return 'This CUE track is virtual. Use ${context.l10n.cueSplitButton} first.'; } void _showCueVirtualTrackSnackBar(BuildContext context) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(_cueVirtualTrackGuidance(context)))); } void _hideCurrentSnackBar() { ScaffoldMessenger.of(context).hideCurrentSnackBar(); } String get _l10nCueSplitFailed => context.l10n.cueSplitFailed; String get _l10nCueSplitNoAudioFile => context.l10n.cueSplitNoAudioFile; String _l10nCueSplitSplitting(int current, int total) { return context.l10n.cueSplitSplitting(current, total); } String _l10nCueSplitSuccess(int count) { return context.l10n.cueSplitSuccess(count); } void _showSnackBarMessage(String message) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(message))); } void _showLongSnackBarMessage(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), duration: const Duration(seconds: 60)), ); } String _formatPathForDisplay(String pathOrUri) { if (_isLocalItem || !pathOrUri.startsWith('content://')) { return formatSafUriForDisplay(pathOrUri); } final item = _downloadItem!; final settings = ref.read(settingsProvider); final sameSelectedTree = item.downloadTreeUri != null && item.downloadTreeUri!.isNotEmpty && item.downloadTreeUri == settings.downloadTreeUri; return buildSafFileDisplayPath( pathOrUri: pathOrUri, treeUri: item.downloadTreeUri, treeDisplayPath: sameSelectedTree ? settings.downloadDirectory : null, relativeDir: item.safRelativeDir, fileName: item.safFileName, ); } }