refactor: dispatch on MusicServices constants instead of raw service ids

Service ids ('deezer', 'tidal', 'qobuz', 'spotify', 'amazon', 'local')
were compared as raw string literals in every dispatch chain. Introduce
MusicServices constants and use them at the comparison/switch sites so
a typo becomes a compile error instead of a silently dead branch.
This commit is contained in:
zarzet
2026-07-10 04:39:35 +07:00
parent c24a72c302
commit 19f69a6090
6 changed files with 53 additions and 34 deletions
+15
View File
@@ -0,0 +1,15 @@
/// Canonical service / provider identifiers shared across the app.
///
/// These values must stay in sync with the provider ids reported by the Go
/// backend and by extensions; use them instead of raw string literals when
/// comparing or dispatching on a service id.
abstract final class MusicServices {
static const spotify = 'spotify';
static const deezer = 'deezer';
static const tidal = 'tidal';
static const qobuz = 'qobuz';
static const amazon = 'amazon';
/// Pseudo-service for tracks that only exist in the local library.
static const local = 'local';
}
@@ -4,6 +4,7 @@ import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/widgets/priority_settings_scaffold.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
import 'package:spotiflac_android/constants/music_services.dart';
class LyricsProviderPriorityPage extends ConsumerStatefulWidget {
const LyricsProviderPriorityPage({super.key});
@@ -215,13 +216,13 @@ class _LyricsProviderPriorityPageState
description: context.l10n.lyricsProviderQqMusicDesc,
icon: Icons.queue_music,
);
case 'spotify':
case MusicServices.spotify:
return _LyricsProviderInfo(
name: 'Spotify',
description: context.l10n.lyricsProviderExtensionDesc,
icon: Icons.graphic_eq,
);
case 'deezer':
case MusicServices.deezer:
return _LyricsProviderInfo(
name: 'Deezer',
description: context.l10n.lyricsProviderExtensionDesc,
+6 -6
View File
@@ -272,11 +272,11 @@ extension _TrackMetadataFileActions on _TrackMetadataScreenState {
IconData _getServiceIcon(String service) {
switch (service.toLowerCase()) {
case 'tidal':
case MusicServices.tidal:
return Icons.waves;
case 'qobuz':
case MusicServices.qobuz:
return Icons.album;
case 'amazon':
case MusicServices.amazon:
return Icons.shopping_cart;
default:
return Icons.cloud_download;
@@ -285,11 +285,11 @@ extension _TrackMetadataFileActions on _TrackMetadataScreenState {
Color _getServiceColor(String service, ColorScheme colorScheme) {
switch (service.toLowerCase()) {
case 'tidal':
case MusicServices.tidal:
return const Color(0xFF0077B5);
case 'qobuz':
case MusicServices.qobuz:
return const Color(0xFF0052CC);
case 'amazon':
case MusicServices.amazon:
return const Color(0xFFFF9900);
default:
return colorScheme.primary;
+12 -12
View File
@@ -368,15 +368,15 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
fallbackService: _service.toLowerCase(),
);
String buttonLabel;
if (openService == 'deezer') {
if (openService == MusicServices.deezer) {
buttonLabel = context.l10n.trackOpenInDeezer;
} else if (openService == 'amazon') {
} else if (openService == MusicServices.amazon) {
buttonLabel = context.l10n.trackOpenInService(
'Amazon Music',
);
} else if (openService == 'tidal') {
} else if (openService == MusicServices.tidal) {
buttonLabel = context.l10n.trackOpenInService('Tidal');
} else if (openService == 'qobuz') {
} else if (openService == MusicServices.qobuz) {
buttonLabel = context.l10n.trackOpenInService('Qobuz');
} else {
buttonLabel = context.l10n.trackOpenInSpotify;
@@ -417,19 +417,19 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
Uri? appUri;
String serviceName;
if (openService == 'deezer') {
if (openService == MusicServices.deezer) {
webUrl = 'https://www.deezer.com/track/$rawId';
appUri = Uri.parse('deezer://www.deezer.com/track/$rawId');
serviceName = 'Deezer';
} else if (openService == 'amazon') {
} else if (openService == MusicServices.amazon) {
webUrl = 'https://music.amazon.com/search/$rawId';
appUri = Uri.parse('amznm://search/$rawId');
serviceName = 'Amazon Music';
} else if (openService == 'tidal') {
} else if (openService == MusicServices.tidal) {
webUrl = 'https://listen.tidal.com/track/$rawId';
appUri = Uri.parse('tidal://track/$rawId');
serviceName = 'Tidal';
} else if (openService == 'qobuz') {
} else if (openService == MusicServices.qobuz) {
webUrl = 'https://play.qobuz.com/track/$rawId';
appUri = Uri.parse('qobuz://track/$rawId');
serviceName = 'Qobuz';
@@ -518,13 +518,13 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
final cleanId = _displayServiceTrackId(_spotifyId!);
String idLabel;
switch (idService) {
case 'deezer':
case MusicServices.deezer:
idLabel = 'Deezer ID';
case 'amazon':
case MusicServices.amazon:
idLabel = 'Amazon ASIN';
case 'tidal':
case MusicServices.tidal:
idLabel = 'Tidal ID';
case 'qobuz':
case MusicServices.qobuz:
idLabel = 'Qobuz ID';
default:
idLabel = 'Spotify ID';
+11 -10
View File
@@ -30,6 +30,7 @@ import 'package:spotiflac_android/utils/nav_bar_inset.dart';
import 'package:spotiflac_android/widgets/audio_analysis_widget.dart';
import 'package:spotiflac_android/widgets/cached_cover_image.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
import 'package:spotiflac_android/constants/music_services.dart';
part 'track_metadata_edit_sheet.dart';
part 'track_metadata_cards.dart';
@@ -787,7 +788,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
String? get _localCoverPath =>
_isLocalItem ? _localLibraryItem!.coverPath : null;
String? get _spotifyId => _isLocalItem ? null : _downloadItem!.spotifyId;
String get _service => _isLocalItem ? 'local' : _downloadItem!.service;
String get _service => _isLocalItem ? MusicServices.local : _downloadItem!.service;
DateTime get _addedAt {
if (_isLocalItem) {
final modTime = _localLibraryItem!.fileModTime;
@@ -915,19 +916,19 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
if (raw.isEmpty) return fallbackService;
final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
if (raw.startsWith('deezer:')) return 'deezer';
if (raw.startsWith('tidal:')) return 'tidal';
if (raw.startsWith('qobuz:')) return 'qobuz';
if (raw.startsWith('spotify:')) return 'spotify';
if (spotifyTrackIdPattern.hasMatch(raw)) return 'spotify';
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 'spotify';
if (host.contains('deezer.com')) return 'deezer';
if (host.contains('tidal.com')) return 'tidal';
if (host.contains('qobuz.com')) return 'qobuz';
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;
+6 -4
View File
@@ -1,10 +1,12 @@
import 'package:spotiflac_android/constants/music_services.dart';
/// Maps a legacy prefixed resource id (e.g. "deezer:123") to its provider id,
/// or null when the value carries no known provider prefix.
String? legacyProviderIdFromResourceId(String value) {
if (value.startsWith('deezer:')) return 'deezer';
if (value.startsWith('qobuz:')) return 'qobuz';
if (value.startsWith('tidal:')) return 'tidal';
if (value.startsWith('spotify:')) return 'spotify';
if (value.startsWith('deezer:')) return MusicServices.deezer;
if (value.startsWith('qobuz:')) return MusicServices.qobuz;
if (value.startsWith('tidal:')) return MusicServices.tidal;
if (value.startsWith('spotify:')) return MusicServices.spotify;
return null;
}