#102: Moving Models and constants to their own library

This commit is contained in:
kdeng00
2024-07-27 22:12:32 -04:00
parent 8900afdfd2
commit 8fdc3cfe40
50 changed files with 25 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
using Icarus.Models;
using Icarus.Database.Contexts;
namespace Icarus.Controllers.Managers;
public class AlbumManager : BaseManager
{
#region Fields
private AlbumContext? _albumContext;
#endregion
#region Properties
#endregion
#region Constructors
public AlbumManager(IConfiguration config)
{
_config = config;
_connectionString = _config.GetConnectionString("DefaultConnection");
_albumContext = new AlbumContext(_connectionString!);
}
#endregion
#region Methods
public void SaveAlbumToDatabase(ref Song song)
{
_logger.Info("Starting process to save the album record of the song to the database");
var album = new Album();
album.Title = song.AlbumTitle;
album.AlbumArtist = song.Artist;
album.Year = song.Year!.Value;
var albumTitle = song.AlbumTitle;
var albumArtist = song.Artist;
var albumRetrieved = _albumContext!.Albums!.FirstOrDefault(alb => alb.Title!.Equals(albumTitle) && alb.AlbumArtist!.Equals(albumArtist));
if (albumRetrieved == null)
{
album.SongCount = 1;
_albumContext.Add(album);
_albumContext.SaveChanges();
Console.WriteLine($"Album Id {album.Id}");
}
else
{
album.Id = albumRetrieved.Id;
}
song.AlbumId = album.Id;
}
public void DeleteAlbumFromDatabase(Song song)
{
var album = _albumContext!.Albums!.FirstOrDefault(alb => alb.Title!.Equals(song.AlbumTitle));
if (album == null)
{
_logger.Info("Cannot delete the album record because it does not exist");
return;
}
DeleteAlbumFromDb(album);
}
public Album UpdateAlbumInDatabase(Song oldSong, Song newSong)
{
var albumRecord = _albumContext!.Albums!.FirstOrDefault(alb => alb.Title!.Equals(oldSong.AlbumTitle));
var oldAlbumTitle = oldSong.AlbumTitle;
var oldAlbumArtist = oldSong.Artist;
var newAlbumTitle = newSong.AlbumTitle;
var newAlbumArtist = newSong.Artist;
var info = string.Empty;
if (string.IsNullOrEmpty(newAlbumArtist))
newAlbumArtist = oldAlbumArtist;
if (string.IsNullOrEmpty(newAlbumTitle))
newAlbumTitle = oldAlbumTitle;
if ((string.IsNullOrEmpty(newAlbumTitle) && string.IsNullOrEmpty(newAlbumArtist) ||
oldAlbumTitle!.Equals(newAlbumTitle) && oldAlbumArtist!.Equals(newAlbumArtist)))
{
_logger.Info("No change to the song's album");
return albumRecord!;
}
info = "Change to the song's album";
_logger.Info(info);
var existingAlbumRecord = _albumContext.Albums!.FirstOrDefault(alb => alb.Title!.Equals(oldSong.AlbumTitle));
if (existingAlbumRecord == null)
{
_logger.Info("Creating new album record");
var newAlbumRecord = new Album
{
Title = newAlbumTitle,
AlbumArtist = newAlbumArtist,
Year = newSong.Year!.Value
};
_albumContext.Add(newAlbumRecord);
_albumContext.SaveChanges();
return newAlbumRecord;
}
else
{
_logger.Info("Updating existing album record");
existingAlbumRecord = _albumContext.Albums!.FirstOrDefault(alb => alb.Title!.Equals(newSong.AlbumTitle));
existingAlbumRecord!.AlbumArtist = newAlbumArtist;
_albumContext.Update(existingAlbumRecord);
_albumContext.SaveChanges();
return existingAlbumRecord;
}
}
private void DeleteAlbumFromDb(Album album)
{
if (SongsInAlbum(album) <= 1)
{
_albumContext!.Remove(album);
_albumContext.SaveChanges();
}
}
private int SongsInAlbum(Album album)
{
var sngContext = new SongContext(_connectionString!);
var songs = sngContext!.Songs!.Where(sng => sng.AlbumId == album.Id).ToList();
return songs.Count;
}
#endregion
}
@@ -0,0 +1,128 @@
using Icarus.Models;
using Icarus.Database.Contexts;
namespace Icarus.Controllers.Managers;
public class ArtistManager : BaseManager
{
#region Fields
private ArtistContext? _artistContext;
#endregion
#region Properties
#endregion
#region Constructors
public ArtistManager(IConfiguration config)
{
_config = config;
_connectionString = _config.GetConnectionString("DefaultConnection");
_artistContext = new ArtistContext(_connectionString!);
}
#endregion
#region Methods
public void SaveArtistToDatabase(ref Song song)
{
_logger.Info("Starting process to save the artist record of the song to the database");
var artist = new Artist
{
Name = song.Artist,
SongCount = 1
};
var artistRetrieved = _artistContext!.Artists.FirstOrDefault(art => art.Name!.Equals(artist.Name));
if (artistRetrieved == null)
{
artist.SongCount = 1;
_artistContext.Add(artist);
_artistContext.SaveChanges();
}
else
{
artist.Id = artistRetrieved.Id;
}
song.ArtistId = artist.Id;
}
public Artist UpdateArtistInDatabase(Song oldSongRecord, Song newSongRecord)
{
var oldArtistRecord = _artistContext!.Artists.FirstOrDefault(art => art.Name!.Equals(oldSongRecord.AlbumTitle));
var oldArtistName = oldArtistRecord!.Name;
var newArtistName = newSongRecord.Artist;
if (string.IsNullOrEmpty(newArtistName) || oldArtistName!.Equals(newArtistName))
{
_logger.Info("No change to the song's Artist");
return oldArtistRecord;
}
_logger.Info("Change to the song's record found");
if (oldArtistRecord.SongCount <= 1)
{
_logger.Info("Deleting artist record that no longer has any songs");
_artistContext.Remove(oldArtistRecord);
_artistContext.SaveChanges();
}
if (!(_artistContext.Artists.FirstOrDefault(art => art.Name!.Equals(oldSongRecord.AlbumTitle)) != null))
{
_logger.Info("Creating new artist record");
var newArtistRecord = new Artist
{
Name = newArtistName
};
_artistContext.Add(newArtistRecord);
_artistContext.SaveChanges();
return newArtistRecord;
}
else
{
_logger.Info("Updating existing artist record");
var existingArtistRecord = _artistContext.Artists.FirstOrDefault(art => art.Name!.Equals(newSongRecord.AlbumTitle));
_artistContext.Update(existingArtistRecord!);
_artistContext.SaveChanges();
return existingArtistRecord!;
}
}
public void DeleteArtistFromDatabase(Song song)
{
if (!(_artistContext!.Artists.FirstOrDefault(art => art.Name!.Equals(song.Artist)) != null))
{
_logger.Info("Cannot delete the artist record because it does not exist");
return;
}
var artist = _artistContext.Artists.FirstOrDefault(art => art.Name!.Equals(song.Artist));
if (SongsOfArtist(artist!) <= 1)
{
_artistContext!.Remove(artist!);
_artistContext.SaveChanges();
}
}
private int SongsOfArtist(Artist artist)
{
var sngContext = new SongContext(_connectionString!);
var songs = sngContext.Songs!.Where(sng => sng.ArtistId == artist.Id).ToList();
return songs.Count;
}
#endregion
}
@@ -0,0 +1,13 @@
using Microsoft.Extensions.Configuration;
using NLog;
namespace Icarus.Controllers.Managers;
public class BaseManager
{
#region Fields
protected static Logger _logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
protected IConfiguration? _config;
protected string? _connectionString;
#endregion
}
@@ -0,0 +1,182 @@
using Icarus.Constants;
using Icarus.Controllers.Utilities;
using Icarus.Database.Contexts;
using Icarus.Models;
namespace Icarus.Controllers.Managers;
public class CoverArtManager : BaseManager
{
#region Fields
private string? _rootCoverArtPath;
private CoverArtContext? _coverArtContext;
private byte[]? _stockCoverArt = null;
private const string? _filename = "CoverArt.png";
#endregion
#region Constructors
public CoverArtManager(IConfiguration config)
{
_config = config;
_connectionString = _config.GetConnectionString("DefaultConnection");
_rootCoverArtPath = _config.GetValue<string>("CoverArtPath");
Initialize();
}
#endregion
#region Methods
public void SaveCoverArtToDatabase(ref Song song, ref CoverArt coverArt)
{
_logger.Info("Saving cover art record to the database");
_coverArtContext!.Add(coverArt);
_coverArtContext.SaveChanges();
song.CoverArtId = coverArt.Id;
}
public void DeleteCoverArtFromDatabase(CoverArt coverArt)
{
_logger.Info("Attempting to delete cover art from the database");
_coverArtContext!.Attach(coverArt);
_coverArtContext.Remove(coverArt);
_coverArtContext.SaveChanges();
}
public void DeleteCoverArt(CoverArt coverArt)
{
try
{
var stockCoverArtPath = _rootCoverArtPath + _filename;
if (!string.Equals(stockCoverArtPath, coverArt.ImagePath(),
StringComparison.CurrentCultureIgnoreCase))
{
_logger.Info("Song does not contain the stock cover art");
File.Delete(coverArt.ImagePath());
_logger.Info("Cover art deleted from the filesystem");
}
else
{
_logger.Info("Song contains the stock cover art");
_logger.Info("Will not delete from from the filesystem");
}
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
}
public CoverArt? SaveCoverArt(Song song)
{
try
{
var dirMgr = new DirectoryManager(_rootCoverArtPath!);
var defaultExtension = ".png";
dirMgr.CreateDirectory(song);
var coverArt = new CoverArt
{
SongTitle = song.Title!
};
var segment = coverArt.GenerateFilename(0);
coverArt.Directory = dirMgr.SongDirectory;
coverArt.Filename = segment + defaultExtension;
var metaData = new MetadataRetriever();
var imgBytes = metaData.RetrieveCoverArtBytes(song);
if (imgBytes != null)
{
_logger.Info("Saving cover art to the filesystem");
File.WriteAllBytes(coverArt.ImagePath(), imgBytes);
}
else
{
_logger.Info("Song has no cover art, applying stock cover art");
var coverArtFilePath = _rootCoverArtPath + $"{segment}{defaultExtension}";
coverArt.Directory = DirectoryPaths.CoverArtDirectory;
coverArt.Filename = DirectoryPaths.CoverArtFilename;
metaData.UpdateCoverArt(song, coverArt);
coverArt.Directory = this._rootCoverArtPath;
coverArt.Filename = $"{segment}{defaultExtension}";
File.WriteAllBytes(coverArt.ImagePath(), _stockCoverArt!);
}
coverArt.Type = metaData.CoverArtFileExtensionType(coverArt);
if (string.IsNullOrEmpty(coverArt.Type))
{
Console.WriteLine("File type is empty");
Console.WriteLine($"Directory: {coverArt.Directory}");
Console.WriteLine($"Filename: {coverArt.Filename}");
}
return coverArt;
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
return null;
}
public CoverArt SaveCoverArt(IFormFile data, Song song)
{
var cover = new CoverArt { SongTitle = song.Title };
try
{
MetadataRetriever metaData = new MetadataRetriever();
var dirMgr = new DirectoryManager(_rootCoverArtPath!);
cover.Type = metaData.FileExtensionType(data);
var defaultExtension = "." + cover.Type;
dirMgr.CreateDirectory(song);
var segment = cover.GenerateFilename(0);
var imagePath = dirMgr.SongDirectory + segment + defaultExtension;
cover.Directory = dirMgr.SongDirectory;
cover.Filename = segment + defaultExtension;
using (var fileStream = new FileStream(cover.ImagePath(), FileMode.Create))
{
data.CopyTo(fileStream);
}
}
catch (Exception ex)
{
_logger.Error(ex.Message, "An error occurred");
}
return cover;
}
public CoverArt GetCoverArt(Song song)
{
var title = song.Title;
var cov = _coverArtContext!.CoverArtImages!.FirstOrDefault(cov => cov.SongTitle!.Equals(title));
return cov!;
}
private void Initialize()
{
_coverArtContext = new CoverArtContext(_connectionString!);
var path = DirectoryPaths.CoverArtDirectory + DirectoryPaths.CoverArtFilename;
if (System.IO.File.Exists(path))
_stockCoverArt = File.ReadAllBytes(path);
if (!File.Exists(_rootCoverArtPath + _filename))
{
File.WriteAllBytes(_rootCoverArtPath + _filename,
_stockCoverArt!);
Console.WriteLine("Copied Stock Cover Art");
}
}
#endregion
}
@@ -0,0 +1,261 @@
using Icarus.Models;
using Icarus.Types;
namespace Icarus.Controllers.Managers;
// NOTE: Do not use metadata for the song's metadata
public class DirectoryManager : BaseManager
{
#region Fields
private Song? _song;
private string? _rootSongDirectory;
private string? _songDirectory;
#endregion
#region Properties
public string? SongDirectory
{
get => _songDirectory;
set => _songDirectory = value;
}
#endregion
#region Constructors
public DirectoryManager(IConfiguration config, Song song)
{
_config = config;
_song = song;
Initialize();
}
public DirectoryManager(IConfiguration config)
{
_config = config;
Initialize();
}
public DirectoryManager(string rootDirectory)
{
_rootSongDirectory = rootDirectory;
}
#endregion
#region Methods
// Does not include extension
public static string GenerateFilename(int length)
{
string chars = Constants.DirectoryPaths.FILENAME_CHARACTERS;
var random = new Random();
return new string(Enumerable.Repeat(chars, length).Select(s =>
s[random.Next(s.Length)]).ToArray());
}
public static string GenerateDownloadFilename(int length, string extension, string title, bool? randomize)
{
if (randomize.HasValue && randomize.Value)
{
return GenerateFilename(length) + extension;
}
else
{
return title + extension;
}
}
public void CreateDirectory()
{
this.CreateDirectory(_song!);
}
public void CreateDirectory(Song song)
{
this._song = song;
try
{
_songDirectory = AlbumDirectory();
if (!Directory.Exists(_songDirectory))
{
Directory.CreateDirectory(_songDirectory);
Console.WriteLine($"The directory has been created");
}
Console.WriteLine($"The song will be saved in the following" +
$" directory: {_songDirectory}");
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
}
public void DeleteEmptyDirectories()
{
try
{
var albumDirectory = AlbumDirectory();
var artistDirectory = ArtistDirectory();
if (IsDirectoryEmpty(albumDirectory))
{
Directory.Delete(albumDirectory);
Console.WriteLine($"directory {albumDirectory} deleted");
}
if (IsDirectoryEmpty(artistDirectory))
{
Directory.Delete(artistDirectory);
Console.WriteLine($"directory {artistDirectory} deleted");
}
}
catch (Exception ex)
{
var exMsg = ex.Message;
Console.WriteLine($"An error occurred {exMsg}");
}
}
public void DeleteEmptyDirectories(Song song)
{
try
{
var albumDirectory = AlbumDirectory(song);
var artistDirectory = ArtistDirectory(song);
if (IsDirectoryEmpty(albumDirectory))
{
Directory.Delete(albumDirectory);
_logger.Info("Album directory deleted");
}
if (IsDirectoryEmpty(artistDirectory))
{
Directory.Delete(artistDirectory);
_logger.Info("Artist directory deleted");
}
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
}
public string RetrieveAlbumPath(Song song)
{
_logger.Info("Retrieving album song path");
var albumPath = string.Empty;
albumPath = AlbumDirectory(song);
return albumPath;
}
public string RetrieveArtistPath(Song song)
{
_logger.Info("Retrieving artist path");
var artistPath = string.Empty;
artistPath = ArtistDirectory(song);
return artistPath;
}
public string GenerateSongPath(Song song)
{
_logger.Info("Generating song path");
var artistPath = ArtistDirectory(song);
var albumPath = AlbumDirectory(song);
GenerateDirectories(new List<DirEnt>{
new DirEnt
{
Pre = "Artist path does not exist",
Path = artistPath,
Post = "Creating artist path"
},
new DirEnt
{
Pre = "Album path does not exist",
Path = albumPath,
Post = "Created album path"
}
});
return albumPath;
}
private class DirEnt
{
public string? Pre { get; set;}
public string? Path { get; set; }
public string? Post { get; set; }
}
private void GenerateDirectories(List<DirEnt> dirs)
{
foreach (var di in dirs)
{
_logger.Info(di.Pre);
Directory.CreateDirectory(di.Path!);
_logger.Info(di.Post);
}
}
private void Initialize(DirectoryType dirTypes = DirectoryType.Music)
{
switch (dirTypes)
{
case DirectoryType.Music:
_rootSongDirectory = _config!.GetValue<string>("RootMusicPath")!;
break;
case DirectoryType.CoverArt:
_rootSongDirectory = _config!.GetValue<string>("CoverArtPath")!;
break;
}
}
private bool IsDirectoryEmpty(string path)
{
return !Directory.EnumerateFileSystemEntries(path).Any();
}
private string AlbumDirectory()
{
return AlbumDirectory(_song!);
}
private string AlbumDirectory(Song song)
{
var directory = ArtistDirectory(song);
var segment = SerializeValue(song.AlbumTitle!);
directory += $@"{segment}/";
Console.WriteLine($"Album directory {directory}");
return directory;
}
private string ArtistDirectory()
{
return ArtistDirectory(_song!);
}
private string ArtistDirectory(Song song)
{
var directory = _rootSongDirectory;
var segment = SerializeValue(song.Artist!);
directory += $@"{segment}/";
Console.WriteLine($"Artist directory {directory}");
return directory;
}
private string SerializeValue(string value)
{
const int length = 15;
const string chars = "ABCDEF0123456789";
var random = new Random();
var output = new string(Enumerable.Repeat(chars, length).Select(s =>
s[random.Next(s.Length)]).ToArray());
return output;
}
#endregion
}
+128
View File
@@ -0,0 +1,128 @@
using Icarus.Models;
using Icarus.Database.Contexts;
namespace Icarus.Controllers.Managers;
public class GenreManager : BaseManager
{
#region Fields
private GenreContext? _genreContext;
#endregion
#region Properties
#endregion
#region Constructors
public GenreManager(IConfiguration config)
{
_config = config;
_connectionString = _config.GetConnectionString("DefaultConnection");
_genreContext = new GenreContext(_connectionString!);
}
#endregion
#region Methods
public void SaveGenreToDatabase(ref Song song)
{
_logger.Info("Starting process to save the genre record of the song to the database");
var genre = new Genre
{
GenreName = song.Genre,
SongCount = 1
};
var genreName = song.Genre;
var genreRetrieved = _genreContext!.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(genreName));
if (genreRetrieved == null)
{
_genreContext.Add(genre);
_genreContext.SaveChanges();
}
else
{
genre.Id = genreRetrieved.Id;
}
song.GenreId = genre.Id;
}
public Genre UpdateGenreInDatabase(Song oldSongRecord, Song newSongRecord)
{
var oldGenreRecord = _genreContext!.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(oldSongRecord.Genre));
var oldGenreName = oldGenreRecord!.GenreName;
var newGenreName = newSongRecord.Genre;
if (string.IsNullOrEmpty(newGenreName) || oldGenreName!.Equals(newGenreName))
{
_logger.Info("No change to the song's Genre");
return oldGenreRecord;
}
_logger.Info("Change to the song's genre found");
if (oldGenreRecord.SongCount <= 1)
{
_logger.Info("Deleting genre record");
_genreContext.Remove(oldGenreRecord);
_genreContext.SaveChanges();
}
if (!(_genreContext.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(oldSongRecord.Genre)) != null))
{
_logger.Info("Creating new genre record");
var newGenreRecord = new Genre
{
GenreName = newGenreName
};
_genreContext.Add(newGenreRecord);
_genreContext.SaveChanges();
return newGenreRecord;
}
else
{
_logger.Info("Updating existing genre record");
var existingGenreRecord = _genreContext.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(oldGenreRecord.GenreName));
_genreContext.Update(existingGenreRecord!);
_genreContext.SaveChanges();
return existingGenreRecord!;
}
}
public void DeleteGenreFromDatabase(Song song)
{
if (!(_genreContext!.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(song.Genre)) != null))
{
_logger.Info("Cannot delete the genre record because it does not exist");
return;
}
var genre = _genreContext.Genres!.FirstOrDefault(gnr => gnr.GenreName!.Equals(song.Genre));
if (SongsInGenre(genre!) <= 1)
{
_genreContext.Remove(genre!);
_genreContext.SaveChanges();
}
}
private int SongsInGenre(Genre genre)
{
var sngContext = new SongContext(_connectionString!);
var songs = sngContext.Songs!.Where(sng => sng.GenreId == genre.Id).ToList();
return songs.Count;
}
#endregion
}
+529
View File
@@ -0,0 +1,529 @@
using NLog;
using Icarus.Controllers.Utilities;
using Icarus.Models;
using Icarus.Database.Contexts;
namespace Icarus.Controllers.Managers;
public class SongManager : BaseManager
{
#region Fields
private string? _tempDirectoryRoot;
private string? _archiveDirectoryRoot;
private string? _compressedSongFilename;
private string? _message;
private SongContext? _songContext;
#endregion
#region Properties
public string? ArchiveDirectoryRoot
{
get => _archiveDirectoryRoot;
set => _archiveDirectoryRoot = value;
}
public string? CompressedSongFilename
{
get => _compressedSongFilename;
set => _compressedSongFilename = value;
}
public string? Message
{
get => _message;
set => _message = value;
}
#endregion
#region Constructors
public SongManager(IConfiguration config)
{
_config = config;
Initialize();
}
public SongManager(IConfiguration config, string tempDirectoryRoot)
{
_config = config;
_tempDirectoryRoot = tempDirectoryRoot;
Initialize();
}
#endregion
#region Methods
public SongResult UpdateSong(Song song)
{
var result = new SongResult();
if (!DoesSongExist(song))
{
result.SongTitle = song.Title;
result.Message = "Song does not exist";
return result;
}
try
{
var oldSongRecord = _songContext!.RetrieveRecord(song);
song.Filename = oldSongRecord.Filename;
song.SongDirectory = oldSongRecord.SongDirectory;
MetadataRetriever updateMetadata = new MetadataRetriever();
updateMetadata.UpdateMetadata(song, oldSongRecord);
var updatedSong = updateMetadata.UpdatedSongRecord;
var albMgr = new AlbumManager(_config!);
var gnrMgr = new GenreManager(_config!);
var artMgr = new ArtistManager(_config!);
var updatedAlbum = albMgr.UpdateAlbumInDatabase(oldSongRecord, updatedSong!);
oldSongRecord.AlbumId = updatedAlbum.Id;
var updatedArtist = artMgr.UpdateArtistInDatabase(oldSongRecord, updatedSong!);
oldSongRecord.ArtistId = updatedArtist.Id;
var updatedGenre = gnrMgr.UpdateGenreInDatabase(oldSongRecord, updatedSong!);
oldSongRecord.GenreId = updatedGenre.Id;
UpdateSongInDatabase(ref oldSongRecord, ref updatedSong!, ref result);
DeleteEmptyDirectories(ref oldSongRecord, ref updatedSong);
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
result.Message = $"An error occurred: {msg}";
result.SongTitle = song.Title;
}
return result;
}
public bool DeleteSongFromFileSystem(Song songMetaData)
{
bool successful = false;
try
{
var songPath = songMetaData.SongPath();
File.Delete(songPath);
successful = true;
DirectoryManager dirMgr = new DirectoryManager(_config!, songMetaData);
dirMgr.DeleteEmptyDirectories();
Console.WriteLine("Song successfully deleted");
}
catch (Exception ex)
{
var exMsg = ex.Message;
}
return successful;
}
public bool DoesSongExist(Song song)
{
if (!_songContext!.DoesRecordExist(song))
{
return false;
}
return true;
}
public void DeleteSong(Song song)
{
try
{
if (DeleteSongFromFilesystem(song, true))
{
_logger.Error("Failed to delete the song");
throw new Exception("Failed to delete the song");
}
_logger.Info("Song deleted from the filesystem");
var coverMgr = new CoverArtManager(_config!);
var coverArt = coverMgr.GetCoverArt(song);
coverMgr.DeleteCoverArt(coverArt);
DeleteSongFromDatabase(song);
coverMgr.DeleteCoverArtFromDatabase(coverArt);
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
}
public async Task SaveSongToFileSystem(IFormFile songFile)
{
try
{
_logger.Info("Starting the process of saving the song to the filesystem");
var song = await SaveSongTemp(songFile);
DirectoryManager dirMgr = new DirectoryManager(_config!, song);
dirMgr.CreateDirectory();
var tempPath = song.SongPath();
song.Filename = song.GenerateFilename(true, AudioFileExtensionsType.WAV);
var filePath = $"{dirMgr.SongDirectory}{song.Filename}";
_logger.Info($"Absolute song path: {filePath}");
await Task.Run(() =>
{
this.MoveSongToFinalDestination(tempPath, filePath);
});
song.SongDirectory = dirMgr.SongDirectory;
var coverMgr = new CoverArtManager(_config!);
var coverArt = coverMgr.SaveCoverArt(song);
SaveSongToDatabase(song, coverArt);
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
}
// Change the name of this method to only focus on wav files
public Song SaveSongToFileSystem(IFormFile songFile, IFormFile coverArtData, Song song)
{
if (string.IsNullOrEmpty(song.SongDirectory))
{
song.SongDirectory = _tempDirectoryRoot;
}
if (string.IsNullOrEmpty(song.Filename))
{
switch (song.AudioType)
{
case "wav":
song.Filename = song.GenerateFilename(true, AudioFileExtensionsType.WAV);
break;
case "flac":
song.Filename = song.GenerateFilename(true, AudioFileExtensionsType.FLAC);
break;
default:
song.Filename = song.GenerateFilename(true, AudioFileExtensionsType.Default);
break;
}
}
_logger.Info($"Temporary directory: {_tempDirectoryRoot}");
var tempPath = song.SongPath();
_logger.Info("Temporary song path: {0}", tempPath);
if (!System.IO.File.Exists(tempPath))
{
using (var filestream = new FileStream(tempPath, FileMode.Create))
{
_logger.Info("Saving song to temporary directory");
songFile.CopyTo(filestream);
}
}
var coverMgr = new CoverArtManager(_config!);
var coverArt = coverMgr.SaveCoverArt(coverArtData, song);
DirectoryManager dirMgr = new DirectoryManager(_config!, song);
dirMgr.CreateDirectory();
song.SongDirectory = dirMgr.SongDirectory;
var filePath = song.SongPath();
_logger.Info($"Absolute song path: {filePath}");
this.MoveSongToFinalDestination(tempPath, filePath);
SaveSongToDatabase(song, coverArt);
return song;
}
public Song SaveFlacSongToFileSystem(IFormFile songFile, IFormFile coverArtData, Song song)
{
// Save temp song (Should already be saved to the filesystem by the time it gets to this method)
// Save cover art
// Update the song's metadata with the song object
// Save song to its final directory
// Save cover art to the database
// Save song to the database
var coverMgr = new CoverArtManager(_config!);
var coverArt = coverMgr.SaveCoverArt(coverArtData, song);
var meta = new Utilities.MetadataRetriever();
meta.UpdateMetadata(song, song);
DirectoryManager dirMgr = new DirectoryManager(_config!, song);
dirMgr.CreateDirectory();
var tempPath = song.SongPath();
song.SongDirectory = dirMgr.SongDirectory;
song.Filename = song.GenerateFilename(true, AudioFileExtensionsType.FLAC);
var filePath = song.SongPath();
_logger.Info($"Absolute song path: {filePath}");
this.MoveSongToFinalDestination(tempPath, filePath);
SaveSongToDatabase(song, coverArt);
return song;
}
private void MoveSongToFinalDestination(string sourcePath, string targetPath)
{
using (var fileStream = new FileStream(targetPath, FileMode.Create))
{
var songBytes = System.IO.File.ReadAllBytes(sourcePath);
try
{
if (System.IO.File.Exists(sourcePath) && System.IO.File.Exists(sourcePath) && fileStream.Length > 0)
{
System.IO.File.Delete(sourcePath);
_logger.Info("Deleted temp song from filesystem: {0}", sourcePath);
}
else
{
fileStream.Write(songBytes, 0, songBytes.Count());
_logger.Info("Saved song to filesystem: {0}", targetPath);
System.IO.File.Delete(sourcePath);
_logger.Info("Deleted temp song from filesystem: {0}", sourcePath);
}
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error($"An error occurred: {msg}");
}
_logger.Info("Song successfully saved to filesystem");
}
}
public async Task<SongData> RetrieveSong(Song songMetaData)
{
var song = new SongData();
try
{
Console.WriteLine("Fetching song from filesystem");
song = await RetrieveSongFromFileSystem(songMetaData);
}
catch (Exception ex)
{
var exMsg = ex.Message;
Console.WriteLine($"An error occurred: {exMsg}");
}
return song;
}
private async Task<SongData> RetrieveSongFromFileSystem(Song details)
{
byte[] uncompressedSong = await System.IO.File.ReadAllBytesAsync(details.SongPath());
return new SongData
{
Data = uncompressedSong
};
}
public async Task<Song> SaveSongTemp(IFormFile songFile)
{
_logger.Info("Assigning song filename");
var song = new Song { SongDirectory = this._tempDirectoryRoot! };
var filename = await song.GenerateFilenameAsync(false) + "-" + songFile.FileName;
song.Filename = filename;
var songPath = song.SongPath();
using (var filestream = new FileStream(songPath, FileMode.Create))
{
_logger.Info("Saving temp song: {0}", songPath);
await songFile.CopyToAsync(filestream);
}
song.DateCreated = DateTime.Now;
return song;
}
private bool SongRecordChanged(Song currentSong, Song songUpdates)
{
var currentTitle = currentSong.Title;
var currentArtist = currentSong.Artist;
var currentAlbum = currentSong.AlbumTitle;
var currentGenre = currentSong.Genre;
var currentYear = currentSong.Year;
if (!currentTitle!.Equals(songUpdates.Title) || !currentArtist!.Equals(songUpdates.Artist) ||
!currentAlbum!.Equals(songUpdates.AlbumTitle) ||
!currentGenre!.Equals(songUpdates.Genre) || currentYear != songUpdates.Year)
return true;
return false;
}
private void DeleteEmptyDirectories(ref Song oldSong, ref Song updatedSong)
{
DirectoryManager mgr = new DirectoryManager(_config!);
_logger.Info("Checking to see if there are any directories to delete");
mgr.DeleteEmptyDirectories(oldSong);
}
private void Initialize()
{
try
{
_connectionString = _config!.GetConnectionString("DefaultConnection");
_songContext = new SongContext(_connectionString!);
}
catch (Exception ex)
{
Console.WriteLine($"Error Occurred: {ex.Message}");
}
}
private void SaveSongToDatabase(Song song, CoverArt? cover)
{
_logger.Info("Starting process to save the song to the database");
var albumMgr = new AlbumManager(_config!);
var artistMgr = new ArtistManager(_config!);
var genreMgr = new GenreManager(_config!);
var coverMgr = new CoverArtManager(_config!);
albumMgr.SaveAlbumToDatabase(ref song);
artistMgr.SaveArtistToDatabase(ref song);
genreMgr.SaveGenreToDatabase(ref song);
var info = "Saving Song to DB";
_logger.Info(info);
_songContext!.Add(song);
_songContext!.SaveChanges();
coverMgr.SaveCoverArtToDatabase(ref song, ref cover!);
}
private bool DeleteSongFromFilesystem(Song song, bool deleteDirectory = false)
{
var songPath = song.SongPath();
_logger.Info("Deleting song from the filesystem");
try
{
System.IO.File.Delete(songPath);
DeleteEmptyDirectories(ref song, ref song);
}
catch(Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred when attempting to delete the song from the filesystem");
return false;
}
return DoesSongExistOnFilesystem(song);
}
private bool DoesSongExistOnFilesystem(Song song)
{
if (!System.IO.File.Exists(song.SongPath()))
{
_logger.Info("Song does not exist on the filesystem");
return false;
}
_logger.Info("Song exists on the filesystem");
return true;
}
private void UpdateSongInDatabase(ref Song oldSongRecord, ref Song newSongRecord, ref SongResult result)
{
var updatedSongRecord = oldSongRecord;
var songContext = new SongContext(_connectionString!);
if (!SongRecordChanged(oldSongRecord, newSongRecord))
{
_logger.Info("No change to the song record");
return;
}
_logger.Info("Changes to song record found");
if (!string.IsNullOrEmpty(newSongRecord.Title))
updatedSongRecord.Title = newSongRecord.Title;
if (!string.IsNullOrEmpty(newSongRecord.AlbumTitle))
{
updatedSongRecord.AlbumTitle = newSongRecord.AlbumTitle;
}
if (!string.IsNullOrEmpty(newSongRecord.Artist))
{
updatedSongRecord.Artist = newSongRecord.Artist;
}
if (!string.IsNullOrEmpty(newSongRecord.Genre))
{
updatedSongRecord.Genre = newSongRecord.Genre;
Console.WriteLine("Genre changed");
Console.WriteLine($"{updatedSongRecord.Genre} {newSongRecord.Genre}");
}
if (newSongRecord.Year != null || newSongRecord.Year > 0)
updatedSongRecord.Year = newSongRecord.Year;
_logger.Info("Applied changes to song record");
_logger.Info("Saving song metadata to the database");
songContext.Update(updatedSongRecord);
songContext.SaveChanges();
newSongRecord = updatedSongRecord;
result.Message = "Successfully updated song";
result.SongTitle = updatedSongRecord.Title!;
}
private void DeleteSongFromDatabase(Song song)
{
_logger.Info("Starting process to delete records related to the song from the database");
var albumMgr = new AlbumManager(_config!);
var artistMgr = new ArtistManager(_config!);
var genreMgr = new GenreManager(_config!);
var sngContext = new SongContext(_connectionString!);
sngContext.Songs!.Remove(song);
sngContext.SaveChanges();
artistMgr.DeleteArtistFromDatabase(song);
albumMgr.DeleteAlbumFromDatabase(song);
genreMgr.DeleteGenreFromDatabase(song);
}
#endregion
}
+281
View File
@@ -0,0 +1,281 @@
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using RestSharp;
using Icarus.Models;
using Microsoft.VisualBasic;
namespace Icarus.Controllers.Managers;
#region Classes
public class TokenManager : BaseManager
{
#region Fields
private string? _clientId;
private string? _clientSecret;
private string? _privateKeyPath = string.Empty;
private string? _privateKey = string.Empty;
private string? _publicKeyPath = string.Empty;
private string? _publicKey = string.Empty;
private string? _audience;
private string? _grantType;
private string? _url;
private string? SUCCESSFUL_TOKEN_MESSAGE = "Successfully retrieved token";
#endregion
#region Properties
#endregion
#region Constructors
public TokenManager(IConfiguration config)
{
_config = config;
InitializeValues();
}
#endregion
#region Methods
public LoginResult RetrieveLoginResult(User user)
{
_logger.Info("Preparing Auth0 API request");
var client = new RestClient(_url!);
var request = new RestRequest("oauth/token", Method.POST);
var tokenRequest = RetrieveTokenRequest();
_logger.Info("Serializing token object into JSON");
var tokenObject = JsonConvert.SerializeObject(tokenRequest);
request.AddParameter("application/json; charset=utf-8",
tokenObject, ParameterType.RequestBody);
request.RequestFormat = DataFormat.Json;
_logger.Info("Sending request");
IRestResponse response = client.Execute(request);
_logger.Info("Response received");
_logger.Info("Deserializing response");
var tokenResult = JsonConvert
.DeserializeObject<TokenTierOne>(response.Content);
_logger.Info("Response deserialized");
return this.InitializeLoginResult(user, tokenResult!);
}
public LoginResult LoginSymmetric(User user)
{
var tokenResult = new TokenTierOne{ TokenType = "JWT" };
var payload = Payload();
payload.Add(new Claim("user_id", user.Id.ToString(), ClaimValueTypes.Integer));
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_config!["JWT:Secret"]!);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("user_id", user.Id.ToString(), ClaimValueTypes.Integer)
// Add more claims as needed
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Issuer = _config["Jwt:Issuer"], // Add this line
Audience = _config["Jwt:Audience"]
};
tokenResult.AccessToken = tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
var expClaim = payload.FirstOrDefault(cl =>
{
return cl.Type.Equals("exp");
});
var expiredDate = DateTime.Parse(expClaim!.Value);
var exp = Math.Floor((expiredDate - DateTime.UnixEpoch).TotalSeconds);
tokenResult.Expiration = Convert.ToInt32(exp);
return this.InitializeLoginResult(user, tokenResult);
}
private LoginResult InitializeLoginResult(User user, TokenTierOne token)
{
return new LoginResult
{
UserId = user.Id, Username = user.Username, Token = token.AccessToken,
TokenType = token.TokenType, Expiration = token.Expiration,
Message = SUCCESSFUL_TOKEN_MESSAGE
};
}
public int RetrieveUserIdFromToken(string token)
{
if (this.ContainsBearer(token))
{
token = this.StripBearer(token);
}
var tokenHandler = new JwtSecurityTokenHandler();
var readTok = tokenHandler.ReadJwtToken(token);
var userId = -1;
foreach (var item in readTok.Payload)
{
if (item.Key == "user_id")
{
userId = Convert.ToInt32(item.Value);
break;
}
}
return userId;
}
private string StripBearer(string token)
{
var start = 6;
var strippedToken = token.Substring(start);
return Strings.Trim(strippedToken);
}
private bool ContainsBearer(string token)
{
return token.Contains("Bearer");
}
private string AllScopes()
{
var allScopes = new List<String>()
{
"download:songs",
"read:song_details",
"upload:songs",
"delete:songs",
"read:albums",
"read:artists",
"update:songs",
"stream:songs",
"read:genre",
"read:year",
"download:cover_art"
};
var scopes = string.Empty;
for (var i = 0; i < allScopes.Count; i++)
{
if (i == allScopes.Count - 1)
{
scopes += allScopes[i];
}
else
{
scopes += allScopes[i] + " ";
}
}
return scopes;
}
private List<Claim> Payload()
{
// TODO: Remove this hard coding
var expLimit = 30;
var currentDate = DateTime.Now;
var expiredDate = currentDate.AddMinutes(expLimit);
var issuer = "http://localhost:5002";
var audience = "http://localhost:5002";
var subject = _config!["JWT:Subject"];
var claim = new List<System.Security.Claims.Claim>()
{
new Claim("scope", AllScopes(), "string"),
new Claim(JwtRegisteredClaimNames.Exp, expiredDate.ToString()),
new Claim(JwtRegisteredClaimNames.Aud, audience),
new Claim(JwtRegisteredClaimNames.Iss, issuer),
new Claim(JwtRegisteredClaimNames.Sub, subject!),
new Claim(JwtRegisteredClaimNames.Iat, currentDate.ToString())
};
return claim;
}
[Obsolete("Deprecated function")]
private async Task<string> ReadKeyContent(string filepath)
{
return await System.IO.File.ReadAllTextAsync(filepath);
}
private TokenRequest RetrieveTokenRequest()
{
_logger.Info("Retrieving token object");
return new TokenRequest
{
ClientId = _clientId, ClientSecret = _clientSecret,
Audience = _audience, GrantType = _grantType
};
}
private void InitializeValues()
{
_logger.Info("Analyzing Auth0 information");
_clientId = _config!["Auth0:ClientId"];
_clientSecret = _config["Auth0:ClientSecret"];
_audience = _config["Auth0:ApiIdentifier"];
_grantType = "client_credentials";
_url = $"https://{_config["Auth0:Domain"]}";
}
#region Testing Methods
// For testing purposes
private void PrintCredentials()
{
Console.WriteLine("Auth0 credentials:");
Console.WriteLine($"Client Id: {_clientId}");
Console.WriteLine($"Client Secret: {_clientSecret}");
Console.WriteLine($"Audience: {_audience}");
Console.WriteLine($"Url: {_url}");
}
#endregion
#endregion
#region Classes
private class TokenRequest
{
[JsonProperty("client_id")]
public string? ClientId { get; set; }
[JsonProperty("client_secret")]
public string? ClientSecret { get; set; }
[JsonProperty("audience")]
public string? Audience { get; set; }
[JsonProperty("grant_type")]
public string? GrantType { get; set; }
}
private class TokenTierOne
{
[JsonProperty("access_token")]
public string? AccessToken { get; set; }
[JsonProperty("expires_in")]
public int Expiration { get; set; }
[JsonProperty("token_type")]
public string? TokenType { get; set; }
}
#endregion
}
#endregion