Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff99492fa | ||
|
|
9dafc53670 | ||
|
|
bfa6374657 | ||
|
|
8b5fbb12b7 | ||
|
|
d62cf28530 |
+19
-18
@@ -9,6 +9,7 @@ import 'services/settings_service.dart';
|
|||||||
import 'services/audio/massiv_audio_handler.dart';
|
import 'services/audio/massiv_audio_handler.dart';
|
||||||
import 'services/auth/auth_manager.dart';
|
import 'services/auth/auth_manager.dart';
|
||||||
import 'services/debug_logger.dart';
|
import 'services/debug_logger.dart';
|
||||||
|
import 'services/music_assistant_api.dart' show MAConnectionState;
|
||||||
import 'theme/theme_provider.dart';
|
import 'theme/theme_provider.dart';
|
||||||
import 'theme/app_theme.dart';
|
import 'theme/app_theme.dart';
|
||||||
import 'theme/system_theme_helper.dart';
|
import 'theme/system_theme_helper.dart';
|
||||||
@@ -267,26 +268,26 @@ class _AppStartupState extends State<AppStartup> {
|
|||||||
|
|
||||||
final provider = context.read<MusicAssistantProvider>();
|
final provider = context.read<MusicAssistantProvider>();
|
||||||
|
|
||||||
// Only attempt connection if not already connected
|
// Connection is handled by MusicAssistantProvider._initialize()
|
||||||
if (!provider.isConnected) {
|
// Just wait for it to complete or timeout
|
||||||
_logger.log('🚀 AppStartup: Auto-connecting to saved server: $serverUrl');
|
_logger.log('🚀 AppStartup: Waiting for provider auto-connection to $serverUrl');
|
||||||
try {
|
|
||||||
// Restore auth credentials before connecting (critical for Authelia)
|
// Give the provider time to connect (it restores credentials and connects in _initialize)
|
||||||
final savedCredentials = await SettingsService.getAuthCredentials();
|
// Check connection state periodically
|
||||||
if (savedCredentials != null) {
|
for (var i = 0; i < 20; i++) {
|
||||||
_logger.log('🚀 AppStartup: Restoring saved auth credentials');
|
await Future.delayed(const Duration(milliseconds: 250));
|
||||||
provider.authManager.deserializeCredentials(savedCredentials);
|
if (provider.isConnected) {
|
||||||
|
_logger.log('🚀 AppStartup: Connection established');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (provider.connectionState == MAConnectionState.error) {
|
||||||
|
_logger.log('🚀 AppStartup: Connection failed with error');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await provider.connectToServer(serverUrl);
|
if (!provider.isConnected) {
|
||||||
_logger.log('🚀 AppStartup: Auto-connection successful');
|
_logger.log('🚀 AppStartup: Connection still pending or failed');
|
||||||
} catch (e) {
|
|
||||||
_logger.log('🚀 AppStartup: Auto-connection failed: $e');
|
|
||||||
// Connection failed, but still show home screen
|
|
||||||
// User can manually reconnect from there
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_logger.log('🚀 AppStartup: Already connected');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class MusicAssistantProvider with ChangeNotifier {
|
|||||||
|
|
||||||
// Local player state
|
// Local player state
|
||||||
bool _isLocalPlayerPowered = true;
|
bool _isLocalPlayerPowered = true;
|
||||||
|
bool _builtinPlayerAvailable = true; // False on MA 2.7.0b20+ (uses Sendspin instead)
|
||||||
StreamSubscription? _localPlayerEventSubscription;
|
StreamSubscription? _localPlayerEventSubscription;
|
||||||
StreamSubscription? _playerUpdatedEventSubscription;
|
StreamSubscription? _playerUpdatedEventSubscription;
|
||||||
Timer? _localPlayerStateReportTimer;
|
Timer? _localPlayerStateReportTimer;
|
||||||
@@ -157,12 +158,16 @@ class MusicAssistantProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// After authentication succeeds, authenticated state will trigger initialization
|
||||||
|
// Don't call _initializeAfterConnection() here - wait for authenticated state
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No auth required, initialize immediately
|
||||||
await _initializeAfterConnection();
|
await _initializeAfterConnection();
|
||||||
} else if (state == MAConnectionState.authenticated) {
|
} else if (state == MAConnectionState.authenticated) {
|
||||||
_logger.log('✅ MA authentication successful');
|
_logger.log('✅ MA authentication successful');
|
||||||
|
// Now safe to initialize since we're authenticated
|
||||||
await _initializeAfterConnection();
|
await _initializeAfterConnection();
|
||||||
} else if (state == MAConnectionState.disconnected) {
|
} else if (state == MAConnectionState.disconnected) {
|
||||||
_availablePlayers = [];
|
_availablePlayers = [];
|
||||||
@@ -364,9 +369,8 @@ class MusicAssistantProvider with ChangeNotifier {
|
|||||||
previousTrackSelectedPlayer();
|
previousTrackSelectedPlayer();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isConnected) {
|
// Player registration is now handled in _initializeAfterConnection()
|
||||||
await _registerLocalPlayer();
|
// which runs after authentication completes (when auth is required)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _tryAdoptGhostPlayer() async {
|
Future<bool> _tryAdoptGhostPlayer() async {
|
||||||
@@ -439,7 +443,20 @@ class MusicAssistantProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
_registrationInProgress = null;
|
_registrationInProgress = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_logger.log('❌ CRITICAL: Player registration failed: $e');
|
// Check if this is because builtin_player API is not available (MA 2.7.0b20+)
|
||||||
|
final errorStr = e.toString();
|
||||||
|
if (errorStr.contains('Invalid command') && errorStr.contains('builtin_player')) {
|
||||||
|
_logger.log('⚠️ Builtin player API not available (MA 2.7.0b20+ uses Sendspin)');
|
||||||
|
_logger.log('ℹ️ Local player registration skipped - use other players (Chromecast, etc)');
|
||||||
|
_builtinPlayerAvailable = false;
|
||||||
|
if (_registrationInProgress != null && !_registrationInProgress!.isCompleted) {
|
||||||
|
_registrationInProgress!.complete();
|
||||||
|
}
|
||||||
|
_registrationInProgress = null;
|
||||||
|
return; // Non-fatal, continue without local player
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.log('❌ Player registration failed: $e');
|
||||||
if (_registrationInProgress != null && !_registrationInProgress!.isCompleted) {
|
if (_registrationInProgress != null && !_registrationInProgress!.isCompleted) {
|
||||||
_registrationInProgress!.completeError(e);
|
_registrationInProgress!.completeError(e);
|
||||||
}
|
}
|
||||||
@@ -461,6 +478,7 @@ class MusicAssistantProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> _reportLocalPlayerState() async {
|
Future<void> _reportLocalPlayerState() async {
|
||||||
if (_api == null) return;
|
if (_api == null) return;
|
||||||
|
if (!_builtinPlayerAvailable) return; // Skip on MA 2.7.0b20+ (no builtin_player API)
|
||||||
|
|
||||||
final playerId = await SettingsService.getBuiltinPlayerId();
|
final playerId = await SettingsService.getBuiltinPlayerId();
|
||||||
if (playerId == null) return;
|
if (playerId == null) return;
|
||||||
|
|||||||
@@ -312,7 +312,25 @@ class MusicAssistantAPI {
|
|||||||
String command, {
|
String command, {
|
||||||
Map<String, dynamic>? args,
|
Map<String, dynamic>? args,
|
||||||
}) async {
|
}) async {
|
||||||
// Allow commands when connected, authenticating, or authenticated
|
// Allow auth commands when connected or authenticating
|
||||||
|
// For all other commands, require authenticated state if auth is required
|
||||||
|
final isAuthCommand = command == 'auth' || command == 'auth/login';
|
||||||
|
|
||||||
|
if (isAuthCommand) {
|
||||||
|
// Auth commands can be sent when connected or authenticating
|
||||||
|
final allowedStates = [
|
||||||
|
MAConnectionState.connected,
|
||||||
|
MAConnectionState.authenticating,
|
||||||
|
];
|
||||||
|
if (!allowedStates.contains(_currentState)) {
|
||||||
|
throw Exception('Not connected to Music Assistant server');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-auth commands require proper authentication state
|
||||||
|
if (_authRequired && !_isAuthenticated) {
|
||||||
|
throw Exception('Not authenticated to Music Assistant server');
|
||||||
|
}
|
||||||
|
|
||||||
final allowedStates = [
|
final allowedStates = [
|
||||||
MAConnectionState.connected,
|
MAConnectionState.connected,
|
||||||
MAConnectionState.authenticating,
|
MAConnectionState.authenticating,
|
||||||
@@ -321,6 +339,7 @@ class MusicAssistantAPI {
|
|||||||
if (!allowedStates.contains(_currentState)) {
|
if (!allowedStates.contains(_currentState)) {
|
||||||
throw Exception('Not connected to Music Assistant server');
|
throw Exception('Not connected to Music Assistant server');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final messageId = _uuid.v4();
|
final messageId = _uuid.v4();
|
||||||
final completer = Completer<Map<String, dynamic>>();
|
final completer = Completer<Map<String, dynamic>>();
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
name: ensemble
|
name: ensemble
|
||||||
description: A minimalistic Music Assistant client built with Flutter
|
description: A minimalistic Music Assistant client built with Flutter
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.4.0+4
|
version: 1.6.1+8
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|||||||
Reference in New Issue
Block a user