Initial commit

This commit is contained in:
2022-03-05 00:43:36 -07:00
commit 151a322276
29 changed files with 1171 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
using TechMedia.Core.Model;
namespace TechMedia.Core.Backend;
public interface IAudioBackend
{
public void JustPlay();
public void PlayFile(string filename);
public void StopPlayback();
public Task<Track?> GetFileMetadata(string filename);
public void RegisterTrackEndHandler(EventHandler<EventArgs> onEndReached);
void RegisterProgressWatcher(IProgress<float> reporter);
void RemoveProgressWatcher(IProgress<float> reporter);
}

View File

@@ -0,0 +1,10 @@
namespace TechMedia.Core.Backend;
public class ProgressListener
{
private readonly IProgress<float> _reporter;
public ProgressListener(IProgress<float> reporter)
{
_reporter = reporter;
}
}

View File

@@ -0,0 +1,22 @@
using TechMedia.Core.Model;
namespace TechMedia.Core;
public interface ITechMediaCore
{
public void Play();
public void StartPlaylistPlayback(IProgress<Track>? trackReporter = null, IProgress<float>? progressReporter = null);
public void Stop();
public void Pause();
public void NextTrack();
public void PreviousTrack();
public void GetNextTrackInfo();
public void GetCurrentTrackInfo();
public void OpenMediaFile(string filename);
public void OpenPlaylistFile();
public Task<int> LoadFiles(List<string> files, IProgress<int>? progress = null);
public List<Track> ShuffleCurrentPlaylist();
public List<Track> GetCurrentPlaylist();
public void RegisterPlaylistChangeListener(IProgress<List<Track>> playlistWatcher);
public Track? SubmitRequest(string filename);
}

View File

@@ -0,0 +1,11 @@
namespace TechMedia.Core.Model;
public class Track
{
public string? Title { get; set; }
public string? Album { get; set; }
public string? AlbumArt { get; set; }
public long Duration { get; set; }
public string Filename { get; set; } = "";
public override string ToString() => $"{Title ?? "?"} - {Album ?? "?"}";
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,158 @@
using TechMedia.Core.Backend;
using TechMedia.Core.Model;
namespace TechMedia.Core
{
public class TechMediaCore : ITechMediaCore
{
private readonly IAudioBackend _backend;
private List<Track> _currentPlaylist = new();
private Queue<Track> _requestQueue = new();
private IProgress<Track>? _trackReporter;
private IProgress<float>? _progressReporter;
private IProgress<List<Track>>? _playlistWatcher;
private int _playbackIndex = 0;
public TechMediaCore(IAudioBackend backend)
{
_backend = backend;
_backend.RegisterTrackEndHandler(TrackEndHandler);
}
public void StartPlaylistPlayback(IProgress<Track>? trackReporter = null, IProgress<float>? 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<int> LoadFiles(List<string> files, IProgress<int>? progress = null)
{
_currentPlaylist = new List<Track>();
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<Track> ShuffleCurrentPlaylist()
{
_backend.StopPlayback();
_playbackIndex = 0;
var random = new Random();
_currentPlaylist = _currentPlaylist.OrderBy(t => random.Next()).ToList();
_playlistWatcher?.Report(_currentPlaylist);
return _currentPlaylist;
}
public List<Track> GetCurrentPlaylist() => _currentPlaylist;
public void RegisterPlaylistChangeListener(IProgress<List<Track>> 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;
}
}
}