Increase XMLDoc coverage

This commit is contained in:
Royce551 2022-04-20 21:07:12 -05:00
parent 98f6943ff4
commit e6de05f971
6 changed files with 262 additions and 4 deletions

View file

@ -8,7 +8,11 @@ using System.Threading.Tasks;
namespace FRESHMusicPlayer.Backends
{
static class AudioBackendFactory
/// <summary>
/// A lower level class for directly getting audio backends.
/// You should probably use the Player class unless there's a reason you need more control.
/// </summary>
public static class AudioBackendFactory
{
private readonly static ContainerConfiguration config = new ContainerConfiguration();
private readonly static CompositionHost container;
@ -36,6 +40,10 @@ namespace FRESHMusicPlayer.Backends
}
}
/// <summary>
/// Adds a directory where FMP will search for audio backends
/// </summary>
/// <param name="path">The file path of the directory</param>
public static void AddDirectory(string path)
{
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
@ -51,6 +59,12 @@ namespace FRESHMusicPlayer.Backends
container = config.CreateContainer();
}
/// <summary>
/// Queries the audio backend system for the most appropiate backend for the supplied path
/// </summary>
/// <param name="filename">The file path to play</param>
/// <returns>The appropiate backend</returns>
/// <exception cref="Exception">Thrown if no backend could be found</exception>
public static async Task<IAudioBackend> CreateAndLoadBackendAsync(string filename)
{
var problems = new List<(BackendLoadResult, Exception)>();

View file

@ -6,32 +6,51 @@ using System.Threading.Tasks;
namespace FRESHMusicPlayer.Backends
{
/// <summary>
/// A metadata provider that uses ATL to get metadata for most audio formats
/// </summary>
public class FileMetadataProvider : IMetadataProvider
{
/// <inheritdoc/>
public string Title => ATLTrack.Title;
// for mp3s, ATL still uses /
/// <inheritdoc/>
public string[] Artists => ATLTrack.Artist.Split(Settings.DisplayValueSeparator, '/');
/// <inheritdoc/>
public string Album => ATLTrack.Album;
/// <inheritdoc/>
public byte[] CoverArt => ATLTrack.EmbeddedPictures.Count != 0 ? ATLTrack.EmbeddedPictures[0].PictureData : null;
/// <inheritdoc/>
public string[] Genres => ATLTrack.Genre.Split(Settings.DisplayValueSeparator, '/');
/// <inheritdoc/>
public int Year => ATLTrack.Year;
/// <inheritdoc/>
public int TrackNumber => ATLTrack.TrackNumber;
/// <inheritdoc/>
public int TrackTotal => ATLTrack.TrackTotal;
/// <inheritdoc/>
public int DiscNumber => ATLTrack.DiscNumber;
/// <inheritdoc/>
public int DiscTotal => ATLTrack.DiscTotal;
/// <inheritdoc/>
public int Length => ATLTrack.Duration;
/// <inheritdoc/>
public Track ATLTrack { get; set; }
/// <summary>
/// Gets metadata for the supplied file path
/// </summary>
/// <param name="path">The file path to query metadata for</param>
public FileMetadataProvider(string path) => ATLTrack = new Track(path);
}
}

View file

@ -6,42 +6,127 @@ using System.Threading.Tasks;
namespace FRESHMusicPlayer.Backends
{
/// <summary>
/// A wrapper for some type of audio library for playing audio
/// </summary>
public interface IAudioBackend : IDisposable
{
/// <summary>
/// Loads the track and gets the backend ready for playback
/// </summary>
/// <param name="file">File path of the track to be played</param>
/// <returns>The result of the load attempt</returns>
Task<BackendLoadResult> LoadSongAsync(string file);
/// <summary>
/// Gets metadata for the given file path
/// </summary>
/// <param name="file">File path of the track to query metadata for</param>
/// <returns>The metadata</returns>
Task<IMetadataProvider> GetMetadataAsync(string file);
/// <summary>
/// Begins playback. If paused, begins playback again
/// </summary>
void Play();
/// <summary>
/// Pauses playback but keeps the backend ready to continue
/// </summary>
void Pause();
/// <summary>
/// The current playhead position in the track
/// </summary>
TimeSpan CurrentTime { get; set; }
/// <summary>
/// The total length of the track
/// </summary>
TimeSpan TotalTime { get; }
/// <summary>
/// The current playback volume, from 0 to 1
/// </summary>
float Volume { get; set; }
/// <summary>
/// Raised when playback stops
/// </summary>
event EventHandler<EventArgs> OnPlaybackStopped;
}
/// <summary>
/// Should be populated with info about the track the backend is playing
/// </summary>
public interface IMetadataProvider
{
/// <summary>
/// Title of the track
/// </summary>
string Title { get; }
/// <summary>
/// Artists of the track
/// </summary>
string[] Artists { get; }
/// <summary>
/// The album the track comes from
/// </summary>
string Album { get; }
/// <summary>
/// Binary image data for the, ideally, front cover of the album
/// </summary>
byte[] CoverArt { get; }
/// <summary>
/// Genres of the track
/// </summary>
string[] Genres { get; }
/// <summary>
/// Year the track was made. If not available, 0
/// </summary>
int Year { get; }
/// <summary>
/// Track's track number. If not available, 0
/// </summary>
int TrackNumber { get; }
/// <summary>
/// Total number of tracks in the album the track is in. If not available, 0
/// </summary>
int TrackTotal { get; }
/// <summary>
/// Disc number of the album the track is in. If not available, 0
/// </summary>
int DiscNumber { get; }
/// <summary>
/// Total number of discs in the album the track is in. If not available, 0
/// </summary>
int DiscTotal { get; }
/// <summary>
/// The length of the track in seconds
/// </summary>
int Length { get; }
}
/// <summary>
/// The result of a backend load attempt
/// </summary>
public enum BackendLoadResult
{
/// <summary>
/// The backend loaded successfully and is really to start playing!
/// </summary>
OK,
/// <summary>
/// The backend does not support the kind of file given
/// </summary>
NotSupported,
/// <summary>
/// An invalid file was given
/// </summary>
Invalid,
/// <summary>
/// There is something wrong with the file given
/// </summary>
Corrupt,
/// <summary>
/// Something else went wrong
/// </summary>
UnknownError
}
}

View file

@ -8,17 +8,47 @@ using System.Text;
namespace FRESHMusicPlayer
{
/// <summary>
/// Wrapper over LiteDB for interacting with the FMP library
/// </summary>
public class Library
{
/// <summary>
/// The actual LiteDB connection, for things that can't be done with the methods here
/// </summary>
public LiteDatabase Database { get; private set; }
/// <summary>
/// Constructs a new library
/// </summary>
/// <param name="library">The actual LiteDB connection</param>
public Library(LiteDatabase library)
{
Database = library;
}
/// <summary>
/// Gets all tracks in the library
/// </summary>
/// <param name="filter">Property of DatabaseTrack to order by</param>
/// <returns>All the tracks in the library</returns>
public List<DatabaseTrack> Read(string filter = "Title") => Database.GetCollection<DatabaseTrack>("tracks").Query().OrderBy(filter).ToList();
/// <summary>
/// Gets all tracks for the given artist
/// </summary>
/// <param name="artist">The artist to get tracks for</param>
/// <returns>All the tracks for the artist</returns>
public List<DatabaseTrack> ReadTracksForArtist(string artist) => Database.GetCollection<DatabaseTrack>("tracks").Query().Where(x => x.Artist == artist).OrderBy("Title").ToList();
/// <summary>
/// Gets all tracks for the given album
/// </summary>
/// <param name="album">The artist to get tracks for</param>
/// <returns>All the tracks for the album</returns>
public List<DatabaseTrack> ReadTracksForAlbum(string album) => Database.GetCollection<DatabaseTrack>("tracks").Query().Where(x => x.Album == album).OrderBy("TrackNumber").ToList();
/// <summary>
/// Gets all tracks for the given playlist. This must be Task.Run()'d due to a quirk that will be fixed next major release
/// </summary>
/// <param name="playlist">The artist to get tracks for</param>
/// <returns>All the tracks for the playlist</returns>
public List<DatabaseTrack> ReadTracksForPlaylist(string playlist)
{
var x = Database.GetCollection<DatabasePlaylist>("playlists").FindOne(y => y.Name == playlist);
@ -26,6 +56,11 @@ namespace FRESHMusicPlayer
foreach (string path in x.Tracks) z.Add(GetFallbackTrack(path));
return z;
}
/// <summary>
/// Adds a track to a playlist
/// </summary>
/// <param name="playlist">The playlist</param>
/// <param name="path">The file path to add</param>
public virtual void AddTrackToPlaylist(string playlist, string path)
{
var x = Database.GetCollection<DatabasePlaylist>("playlists").FindOne(y => y.Name == playlist);
@ -40,12 +75,23 @@ namespace FRESHMusicPlayer
Database.GetCollection<DatabasePlaylist>("playlists").Update(x);
}
}
/// <summary>
/// Removes a track from a playlist
/// </summary>
/// <param name="playlist">The playlist</param>
/// <param name="path">The file path to remove</param>
public virtual void RemoveTrackFromPlaylist(string playlist, string path)
{
var x = Database.GetCollection<DatabasePlaylist>("playlists").FindOne(y => y.Name == playlist);
x.Tracks.Remove(path);
Database.GetCollection<DatabasePlaylist>("playlists").Update(x);
}
/// <summary>
/// Creates a new playlist
/// </summary>
/// <param name="playlist">The name of the playlist</param>
/// <param name="path">An optional track to start the playlist off with</param>
/// <returns>The created playlist</returns>
public virtual DatabasePlaylist CreatePlaylist(string playlist, string path = null)
{
var newplaylist = new DatabasePlaylist
@ -59,7 +105,15 @@ namespace FRESHMusicPlayer
Database.GetCollection<DatabasePlaylist>("playlists").Insert(newplaylist);
return newplaylist;
}
/// <summary>
/// Deletes a playlist
/// </summary>
/// <param name="playlist">The name of the playlist to delete</param>
public virtual void DeletePlaylist(string playlist) => Database.GetCollection<DatabasePlaylist>("playlists").DeleteMany(x => x.Name == playlist);
/// <summary>
/// Imports some tracks to the library
/// </summary>
/// <param name="tracks">The file paths to import</param>
public virtual void Import(string[] tracks)
{
var stufftoinsert = new List<DatabaseTrack>();
@ -72,6 +126,10 @@ namespace FRESHMusicPlayer
}
Database.GetCollection<DatabaseTrack>("tracks").InsertBulk(stufftoinsert);
}
/// <summary>
/// Imports some tracks to the library
/// </summary>
/// <param name="tracks">The file paths to import</param>
public virtual void Import(List<string> tracks)
{
var stufftoinsert = new List<DatabaseTrack>();
@ -82,21 +140,39 @@ namespace FRESHMusicPlayer
}
Database.GetCollection<DatabaseTrack>("tracks").InsertBulk(stufftoinsert);
}
/// <summary>
/// Imports a track to the library
/// </summary>
/// <param name="path">The file path to import</param>
public virtual void Import(string path)
{
var track = new Track(path);
Database.GetCollection<DatabaseTrack>("tracks")
.Insert(new DatabaseTrack { Title = track.Title, Artist = track.Artist, Album = track.Album, Path = track.Path, TrackNumber = track.TrackNumber, Length = track.Duration });
}
/// <summary>
/// Removes a track from the library
/// </summary>
/// <param name="path">The file path to remove</param>
public virtual void Remove(string path)
{
Database.GetCollection<DatabaseTrack>("tracks").DeleteMany(x => x.Path == path);
}
/// <summary>
/// Clears the entire library
/// </summary>
/// <param name="nukePlaylists">Whether to also clear playlists</param>
public virtual void Nuke(bool nukePlaylists = true)
{
Database.GetCollection<DatabaseTrack>("tracks").DeleteAll();
if (nukePlaylists) Database.GetCollection<DatabasePlaylist>("playlists").DeleteAll();
}
/// <summary>
/// Gets a DatabaseTrack for the given file path. Will try getting from the library system first (fast), before
/// falling back to the audio backend system, then finally the default ATL handling
/// </summary>
/// <param name="path">The file path</param>
/// <returns>The track</returns>
public DatabaseTrack GetFallbackTrack(string path)
{
var dbTrack = Database.GetCollection<DatabaseTrack>("tracks").FindOne(x => path == x.Path);
@ -118,19 +194,52 @@ namespace FRESHMusicPlayer
}
}
}
/// <summary>
/// Representation of a track in the database
/// </summary>
public class DatabaseTrack
{
/// <summary>
/// The file path
/// </summary>
public string Path { get; set; }
/// <summary>
/// Title of the track
/// </summary>
public string Title { get; set; }
/// <summary>
/// Semicolon separated list of artists
/// </summary>
public string Artist { get; set; }
/// <summary>
/// Album of the track
/// </summary>
public string Album { get; set; }
/// <summary>
/// The track's track number. If not available, set to 0
/// </summary>
public int TrackNumber { get; set; }
/// <summary>
/// The length of the track in seconds
/// </summary>
public int Length { get; set; }
}
/// <summary>
/// Representation of a playlist in the database
/// </summary>
public class DatabasePlaylist
{
/// <summary>
/// The ID of the playlist
/// </summary>
public int DatabasePlaylistID { get; set; }
/// <summary>
/// The playlist's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// All tracks in the playlist
/// </summary>
public List<string> Tracks { get; set; }
}
}

View file

@ -6,11 +6,25 @@ using System.Threading.Tasks;
namespace FRESHMusicPlayer
{
/// <summary>
/// Playback exception args
/// </summary>
public class PlaybackExceptionEventArgs : EventArgs
{
/// <summary>
/// The actual exception
/// </summary>
public Exception Exception { get; }
/// <summary>
/// A nicely formatted version of the exception for display purposes
/// </summary>
public string Details { get; }
/// <summary>
/// Constructs new playback exception args
/// </summary>
/// <param name="exception">The actual exception</param>
/// <param name="details">A nicely formatted version of the exception for display purposes</param>
public PlaybackExceptionEventArgs(Exception exception, string details)
{
Exception = exception;

View file

@ -8,12 +8,19 @@ using System.Threading.Tasks;
namespace FRESHMusicPlayer
{
/// <summary>
/// The main class for playing audio using FMP Core.
/// Intended to be constructed once and used for the entire app's lifetime
/// </summary>
public class Player
{
/// <summary>
/// The current backend the Player is using for audio playback
/// </summary>
public IAudioBackend CurrentBackend { get; private set; }
/// <summary>
/// Metadata for the current track the Player is playing
/// </summary>
public IMetadataProvider Metadata { get; private set; }
/// <summary>
/// The current playback position./>
@ -44,6 +51,9 @@ namespace FRESHMusicPlayer
}
}
/// <summary>
/// Whether the Player is in a loading state - You cannot start playing a new track while this is true.
/// </summary>
public bool IsLoading { get; private set; } = false;
/// <summary>
/// The current path the Player is playing. Keep in mind that this may not necessarily be a file. For example, it could be the
@ -60,8 +70,14 @@ namespace FRESHMusicPlayer
/// </summary>
public bool Paused { get; set; }
/// <summary>
/// The play queue
/// </summary>
public PlayQueue Queue { get; set; } = new PlayQueue();
/// <summary>
/// Raised whenever the player is beginning to load a track, before SongChanged is raised
/// </summary>
public event EventHandler SongLoading;
/// <summary>
/// Raised whenever a new track is being played.
@ -130,9 +146,10 @@ namespace FRESHMusicPlayer
await PlayAsync();
}
/// <summary>
/// Starts playing the Queue. In order to play a track, you must first add it to the Queue using <see cref="AddQueue(string)"/>.
/// Starts playing the Queue. In order to play a track, you must first add it to the Queue.
/// </summary>
/// <param name="repeat">If true, avoids dequeuing the next track. Not to be used for anything other than the player.</param>
/// <param name="loadMetadata">Whether to load metadata for the track</param>
public async Task PlayAsync(bool repeat = false, bool loadMetadata = true)
{
if (IsLoading) return;
@ -198,7 +215,7 @@ namespace FRESHMusicPlayer
}
/// <summary>
/// Pauses playback without disposing. Can later be resumed with <see cref="ResumeMusic()"/>.
/// Pauses playback without disposing. Can later be resumed with <see cref="Resume()"/>.
/// </summary>
public void Pause()
{