Files
Ensemble/lib/services/local_player_service.dart
Your NameandClaude Opus 4.5 d4e3822fea Clean up logging for release
- Disable animation debugger by default (set _enabled = false)
- Remove verbose position tracker logs (only kept essential error logs)
- Remove verbose local player service logs (kept error logs only)
- Remove verbose audio handler logs (kept error logs only)
- Remove debug print statement from player.dart
- Remove unused _seekPosition variable from expandable_player.dart

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 15:55:48 +00:00

182 lines
4.4 KiB
Dart

import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'debug_logger.dart';
import 'auth/auth_manager.dart';
import '../main.dart' show audioHandler;
/// Metadata for the currently playing track
class TrackMetadata {
final String title;
final String artist;
final String? album;
final String? artworkUrl;
final Duration? duration;
TrackMetadata({
required this.title,
required this.artist,
this.album,
this.artworkUrl,
this.duration,
});
/// Convert to MediaItem for audio_service
MediaItem toMediaItem(String id) {
return MediaItem(
id: id,
title: title,
artist: artist,
album: album ?? '',
duration: duration,
artUri: artworkUrl != null ? Uri.tryParse(artworkUrl!) : null,
);
}
}
/// Service that wraps the global MassivAudioHandler for local playback.
/// This maintains the same interface as before for compatibility with
/// MusicAssistantProvider.
class LocalPlayerService {
final AuthManager authManager;
final _logger = DebugLogger();
bool _isInitialized = false;
// Current track metadata for notifications
TrackMetadata? _currentMetadata;
LocalPlayerService(this.authManager);
// Expose player state streams from the global handler
Stream<PlayerState> get playerStateStream {
return audioHandler.playerStateStream;
}
Stream<Duration> get positionStream {
return audioHandler.positionStream;
}
Stream<Duration?> get durationStream {
return audioHandler.durationStream;
}
// Current state getters
bool get isPlaying {
return audioHandler.isPlaying;
}
double get volume {
return audioHandler.volume;
}
PlayerState get playerState {
return audioHandler.playerState;
}
Duration get position {
return audioHandler.position;
}
Duration get duration {
return audioHandler.duration;
}
Future<void> initialize() async {
if (_isInitialized) return;
// The audioHandler is already initialized in main.dart
// Just mark ourselves as initialized
_isInitialized = true;
}
/// Set metadata for the current track (for notification display)
void setCurrentTrackMetadata(TrackMetadata metadata) {
_currentMetadata = metadata;
}
/// Play a stream URL with authentication headers
Future<void> playUrl(String url) async {
// Ensure player is initialized before playing
if (!_isInitialized) {
await initialize();
}
try {
// Get auth headers from AuthManager
final headers = authManager.getStreamingHeaders();
// Create MediaItem from current metadata
final mediaItem = _currentMetadata?.toMediaItem(url) ?? MediaItem(
id: url,
title: 'Unknown Track',
artist: 'Unknown Artist',
);
await audioHandler.playUrl(
url,
mediaItem,
headers: headers.isNotEmpty ? headers : null,
);
} catch (e, stackTrace) {
_logger.log('LocalPlayerService: Error playing URL: $e');
rethrow;
}
}
/// Update the notification with new track info (without reloading audio)
void updateNotification({
required String id,
required String title,
String? artist,
String? album,
String? artworkUrl,
Duration? duration,
}) {
_currentMetadata = TrackMetadata(
title: title,
artist: artist ?? 'Unknown Artist',
album: album,
artworkUrl: artworkUrl,
duration: duration,
);
}
/// Update the notification while audio is already playing.
/// With audio_service, we can update the MediaItem directly without
/// re-setting the audio source.
Future<void> updateNotificationWhilePlaying(TrackMetadata metadata) async {
final currentItem = audioHandler.currentMediaItem;
final id = currentItem?.id ?? 'unknown';
final newMediaItem = metadata.toMediaItem(id);
audioHandler.updateMediaItem(newMediaItem);
_currentMetadata = metadata;
}
Future<void> play() async {
await audioHandler.play();
}
Future<void> pause() async {
await audioHandler.pause();
}
Future<void> stop() async {
await audioHandler.stop();
}
Future<void> seek(Duration position) async {
await audioHandler.seek(position);
}
/// Set volume (0.0 to 1.0)
Future<void> setVolume(double volume) async {
await audioHandler.setVolume(volume.clamp(0.0, 1.0));
}
void dispose() {
// The global audioHandler is managed by audio_service
// Don't dispose it here
}
}