using TechMedia.Core.Backend; using TechMedia.Core.Model; namespace TechMedia.Core { public class TechMediaCore : ITechMediaCore { private readonly IAudioBackend _backend; private List _currentPlaylist = new(); private Queue _requestQueue = new(); private IProgress? _trackReporter; private IProgress? _progressReporter; private IProgress>? _playlistWatcher; private int _playbackIndex = 0; public TechMediaCore(IAudioBackend backend) { _backend = backend; _backend.RegisterTrackEndHandler(TrackEndHandler); } public void StartPlaylistPlayback(IProgress? trackReporter = null, IProgress? progressReporter = null) { if (trackReporter != null) _trackReporter = trackReporter; if (progressReporter != null) { if (_progressReporter != null) { _backend.RemoveProgressWatcher(_progressReporter); } _progressReporter = progressReporter; _backend.RegisterProgressWatcher(progressReporter); } var firstTrack = _currentPlaylist[_playbackIndex++]; _backend.PlayFile(firstTrack.Filename); _trackReporter?.Report(firstTrack); } private void QueueFilePlayback(string filename) { // LibVLC has a famous bug when calling LibVLC from a LibVLC callback (which is what this method is) that results in a deadlock. // Workaround is to queue the action on another thread. ThreadPool.QueueUserWorkItem(_ => _backend.PlayFile(filename)); } public void TrackEndHandler(object? sender, EventArgs args) { if (_requestQueue.Count > 0) { var requestTrack = _requestQueue.Dequeue(); _trackReporter?.Report(requestTrack); QueueFilePlayback(requestTrack.Filename); } else if (_playbackIndex < _currentPlaylist.Count) { var nextTrack = _currentPlaylist[_playbackIndex++]; _trackReporter?.Report(nextTrack); QueueFilePlayback(nextTrack.Filename); } else { ThreadPool.QueueUserWorkItem(state => { ShuffleCurrentPlaylist(); StartPlaylistPlayback(); }); } } public void Stop() { _backend.StopPlayback(); _playbackIndex = 0; } public void Play() { throw new NotImplementedException(); } public void Pause() { throw new NotImplementedException(); } public void NextTrack() { throw new NotImplementedException(); } public void PreviousTrack() { throw new NotImplementedException(); } public void GetNextTrackInfo() { throw new NotImplementedException(); } public void GetCurrentTrackInfo() { throw new NotImplementedException(); } public void OpenMediaFile(string filename) { _backend.PlayFile(filename); } public void OpenPlaylistFile() { throw new NotImplementedException(); } public async Task LoadFiles(List files, IProgress? progress = null) { _currentPlaylist = new List(); foreach (var (file, index) in files.Select((filename, index) => (filename, index))) { var track = await _backend.GetFileMetadata(file); progress?.Report(index); if (track != null) _currentPlaylist.Add(track); } return _currentPlaylist.Count; } public List ShuffleCurrentPlaylist() { _backend.StopPlayback(); _playbackIndex = 0; var random = new Random(); _currentPlaylist = _currentPlaylist.OrderBy(t => random.Next()).ToList(); _playlistWatcher?.Report(_currentPlaylist); return _currentPlaylist; } public List GetCurrentPlaylist() => _currentPlaylist; public void RegisterPlaylistChangeListener(IProgress> playlistWatcher) { _playlistWatcher = playlistWatcher; } public Track? SubmitRequest(string filename) { var requestedTrack = _currentPlaylist.FirstOrDefault(t => t.Filename.Equals(filename, StringComparison.OrdinalIgnoreCase)); if (requestedTrack != null) _requestQueue.Enqueue(requestedTrack); return requestedTrack; } } }