#93: Add flac support (#101)

* Added script shell to convert wav files to flac

* #93: Minor change to script to convert wav files to flac

* #93: Added more code to for the music file conversion

* #93: Script to convert wav files to flac files is done

* #93: Clean up

* #93: Starting work to implement flac support

* #93: Minor update to conversion script

* #93: Added dotnet manifest file

Includes the dotnet-ef tool

* #93: Updated README and migration script

* #93: Created skeleton for saving flac files

* #93: Making some changes to how the file extension is determined

* #93: Updated gitignore

* #93: Another gitignore update

* #93: Making some changes to the process of saving a song to the filesystem

* #93: Adding SQL script to drop and create database

* #93: Saving uploaded song to a temporary location for processing

* #93: Initializing some properties in the song model earlier

* #93: Uploading wav songs works. Moving on to flac support

* #93: Added more code to save flac files

* #93: Updated some code that affects filename generation for audio files

* #93: Song Model changes and support for different file types

* #93: Working on build errors, removed DotnetZip dependency, and added new Ionic.Zlip.Netstandard dependency

* #93: Reduce build errors

* #93: Removed ID3 dependency

* #93: Cleanup
This commit was merged in pull request #101.
This commit is contained in:
KD
2024-07-27 13:19:12 -04:00
committed by GitHub
parent 4b3fa78336
commit 8900afdfd2
50 changed files with 787 additions and 493 deletions
+5 -5
View File
@@ -12,8 +12,8 @@ namespace Icarus.Controllers.V1;
public class AlbumController : BaseController
{
#region Fields
private readonly ILogger<AlbumController> _logger;
private string _connectionString;
private readonly ILogger<AlbumController>? _logger;
private string? _connectionString;
#endregion
@@ -35,9 +35,9 @@ public class AlbumController : BaseController
[HttpGet]
public IActionResult GetAlbums()
{
var albumContext = new AlbumContext(_connectionString);
var albumContext = new AlbumContext(_connectionString!);
var albums = albumContext.Albums.ToList();
var albums = albumContext.Albums!.ToList();
if (albums.Count > 0)
return Ok(albums);
@@ -50,7 +50,7 @@ public class AlbumController : BaseController
{
Album album = new Album{ Id = id };
var albumContext = new AlbumContext(_connectionString);
var albumContext = new AlbumContext(_connectionString!);
if (albumContext.DoesRecordExist(album))
{
+4 -4
View File
@@ -12,8 +12,8 @@ namespace Icarus.Controllers.V1;
public class ArtistController : BaseController
{
#region Fields
private readonly ILogger<ArtistController> _logger;
private string _connectionString;
private readonly ILogger<ArtistController>? _logger;
private string? _connectionString;
#endregion
@@ -35,7 +35,7 @@ public class ArtistController : BaseController
[HttpGet]
public IActionResult GetArtists()
{
var artistContext = new ArtistContext(_connectionString);
var artistContext = new ArtistContext(_connectionString!);
var artists = artistContext.Artists.ToList();
@@ -50,7 +50,7 @@ public class ArtistController : BaseController
{
Artist artist = new Artist { Id = id };
var artistContext = new ArtistContext(_connectionString);
var artistContext = new ArtistContext(_connectionString!);
if (artistContext.DoesRecordExist(artist))
{
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Icarus.Controllers.V1;
public class BaseController : ControllerBase
{
#region Fiends
protected IConfiguration _config;
protected IConfiguration? _config;
#endregion
+12 -12
View File
@@ -13,8 +13,8 @@ namespace Icarus.Controllers.V1;
public class CoverArtController : BaseController
{
#region Fields
private readonly ILogger<CoverArtController> _logger;
private string _connectionString;
private readonly ILogger<CoverArtController>? _logger;
private string? _connectionString;
#endregion
@@ -32,18 +32,18 @@ public class CoverArtController : BaseController
[HttpGet]
public IActionResult GetCoverArts()
{
var coverArtContext = new CoverArtContext(_connectionString);
var coverArtContext = new CoverArtContext(_connectionString!);
var coverArtRecords = coverArtContext.CoverArtImages.ToList();
var coverArtRecords = coverArtContext.CoverArtImages!.ToList();
if (coverArtRecords == null)
{
_logger.LogInformation("No cover art records");
_logger!.LogInformation("No cover art records");
return NotFound();
}
else
{
_logger.LogInformation("Found cover art records");
_logger!.LogInformation("Found cover art records");
return Ok(coverArtRecords);
}
}
@@ -53,13 +53,13 @@ public class CoverArtController : BaseController
{
var coverArt = new CoverArt { Id = id };
var coverArtContext = new CoverArtContext(_connectionString);
var coverArtContext = new CoverArtContext(_connectionString!);
coverArt = coverArtContext.RetrieveRecord(coverArt);
if (coverArt != null)
{
_logger.LogInformation("Found cover art record");
_logger!.LogInformation("Found cover art record");
var coverArtBytes = System.IO.File.ReadAllBytes(
coverArt.ImagePath());
@@ -68,7 +68,7 @@ public class CoverArtController : BaseController
}
else
{
_logger.LogInformation("Cover art not found");
_logger!.LogInformation("Cover art not found");
return NotFound();
}
}
@@ -76,13 +76,13 @@ public class CoverArtController : BaseController
[HttpGet("data/download/{id}")]
public async Task<IActionResult> Download(int id, [FromQuery] bool? randomizeFilename)
{
var songContext = new SongContext(_connectionString);
var covMgr = new CoverArtManager(this._config);
var songContext = new SongContext(_connectionString!);
var covMgr = new CoverArtManager(this._config!);
var songMetaData = songContext.RetrieveRecord(new Song { Id = id});
var c = covMgr.GetCoverArt(songMetaData);
var filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.JPG_EXTENSION, songMetaData.Title, randomizeFilename);
var filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.JPG_EXTENSION, songMetaData.Title!, randomizeFilename);
var data = await c.GetData();
+5 -5
View File
@@ -12,8 +12,8 @@ namespace Icarus.Controllers.V1;
public class GenreController : BaseController
{
#region Fields
private readonly ILogger<GenreController> _logger;
private string _connectionString;
private readonly ILogger<GenreController>? _logger;
private string? _connectionString;
#endregion
@@ -35,9 +35,9 @@ public class GenreController : BaseController
[HttpGet]
public IActionResult GetGenres()
{
var genreStore = new GenreContext(_connectionString);
var genreStore = new GenreContext(_connectionString!);
var genres = genreStore.Genres.ToList();
var genres = genreStore!.Genres!.ToList();
if (genres.Count > 0)
{
@@ -54,7 +54,7 @@ public class GenreController : BaseController
{
var genre = new Genre { Id = id };
var genreStore = new GenreContext(_connectionString);
var genreStore = new GenreContext(_connectionString!);
if (genreStore.DoesRecordExist(genre))
{
+9 -9
View File
@@ -17,8 +17,8 @@ namespace Icarus.Controllers.V1;
public class LoginController : ControllerBase
{
#region Fields
private string _connectionString;
private IConfiguration _config;
private string? _connectionString;
private IConfiguration? _config;
private ILogger<LoginController> _logger;
#endregion
@@ -41,7 +41,7 @@ public class LoginController : ControllerBase
[HttpPost]
public IActionResult Login([FromBody] User user)
{
var context = new UserContext(_connectionString);
var context = new UserContext(_connectionString!);
_logger.LogInformation("Starting process of validating credentials");
@@ -55,12 +55,12 @@ public class LoginController : ControllerBase
try
{
if (context.Users.FirstOrDefault(usr => usr.Username.Equals(user.Username)) != null)
if (context.Users.FirstOrDefault(usr => usr.Username!.Equals(user.Username)) != null)
{
user = context.Users.FirstOrDefault(usr => usr.Username.Equals(user.Username));
user = context.Users.FirstOrDefault(usr => usr.Username!.Equals(user.Username))!;
var validatePass = new PasswordEncryption();
var validated = validatePass.VerifyPassword(user, password);
var validated = validatePass.VerifyPassword(user!, password!);
if (!validated)
{
loginRes.Message = message;
@@ -71,9 +71,9 @@ public class LoginController : ControllerBase
_logger.LogInformation("Successfully validated user credentials");
TokenManager tk = new TokenManager(_config);
TokenManager tk = new TokenManager(_config!);
loginRes = tk.LoginSymmetric(user);
loginRes = tk.LoginSymmetric(user!);
return Ok(loginRes);
}
@@ -87,7 +87,7 @@ public class LoginController : ControllerBase
catch (Exception ex)
{
_logger.LogError("An error occurred: {0}", ex.Message);
_logger.LogError("Inner Exception: {0}", ex.InnerException.Message);
_logger.LogError("Inner Exception: {0}", ex.InnerException!.Message);
}
return NotFound(loginRes);
+5 -4
View File
@@ -11,7 +11,7 @@ namespace Icarus.Controllers.V1;
public class RegisterController : ControllerBase
{
#region Fields
private IConfiguration _config;
private IConfiguration? _config;
#endregion
@@ -35,11 +35,12 @@ public class RegisterController : ControllerBase
user.Status = "Registered";
user.DateCreated = DateTime.Now;
UserContext context = null;
UserContext? context = null;
try
{
context = new UserContext(_config.GetConnectionString("DefaultConnection"));
var connString = _config!.GetConnectionString("DefaultConnection");
context = new UserContext(connString!);
context.Add(user);
context.SaveChanges();
}
@@ -56,7 +57,7 @@ public class RegisterController : ControllerBase
Username = user.Username
};
if (context.Users.FirstOrDefault(sng => sng.Username.Equals(user.Username)) != null)
if (context!.Users.FirstOrDefault(sng => sng.Username!.Equals(user.Username)) != null)
{
registerResult.Message = "Successful registration";
registerResult.SuccessfullyRegistered = true;
@@ -14,9 +14,9 @@ namespace Icarus.Controllers.V1;
public class SongCompressedDataController : BaseController
{
#region Fields
private string _connectionString;
private string _songTempDir;
private string _archiveDir;
private string? _connectionString;
private string? _songTempDir;
private string? _archiveDir;
#endregion
@@ -39,9 +39,9 @@ public class SongCompressedDataController : BaseController
[HttpGet("{id}")]
public async Task<IActionResult> DownloadCompressedSong(int id, [FromQuery] bool? randomizeFilename)
{
var context = new SongContext(_connectionString);
var context = new SongContext(_connectionString!);
SongCompression cmp = new SongCompression(_archiveDir);
SongCompression cmp = new SongCompression(_archiveDir!);
Console.WriteLine($"Archive directory root: {_archiveDir}");
@@ -49,9 +49,9 @@ public class SongCompressedDataController : BaseController
var sng = context.RetrieveRecord(new Song{ Id = id });
SongData song = await cmp.RetrieveCompressedSong(sng);
var filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.ZIP_EXTENSION, sng.Title, randomizeFilename);
var filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.ZIP_EXTENSION, sng.Title!, randomizeFilename);
return File(song.Data, "application/x-msdownload", filename);
return File(song.Data!, "application/x-msdownload", filename);
}
#endregion
}
+9 -9
View File
@@ -13,9 +13,9 @@ namespace Icarus.Controllers.V1;
public class SongController : BaseController
{
#region Fields
private readonly ILogger<SongController> _logger;
private string _connectionString;
private SongManager _songMgr;
private readonly ILogger<SongController>? _logger;
private string? _connectionString;
private SongManager? _songMgr;
#endregion
@@ -40,11 +40,11 @@ public class SongController : BaseController
public IActionResult GetSongs()
{
Console.WriteLine("Attemtping to retrieve songs");
_logger.LogInformation("Attempting to retrieve songs");
_logger!.LogInformation("Attempting to retrieve songs");
var context = new SongContext(_connectionString);
var context = new SongContext(_connectionString!);
var songs = context.Songs.ToList();
var songs = context.Songs!.ToList();
if (songs.Count > 0)
{
@@ -59,7 +59,7 @@ public class SongController : BaseController
[HttpGet("{id}")]
public IActionResult GetSong(int id)
{
var context = new SongContext(_connectionString);
var context = new SongContext(_connectionString!);
var song = context.RetrieveRecord(new Song{ Id = id });
@@ -76,9 +76,9 @@ public class SongController : BaseController
{
song.Id = id;
Console.WriteLine("Retrieving filepath of song");
_logger.LogInformation("Retrieving filepath of song");
_logger!.LogInformation("Retrieving filepath of song");
if (!_songMgr.DoesSongExist(song))
if (!_songMgr!.DoesSongExist(song))
{
return NotFound(new SongResult
{
+77 -32
View File
@@ -13,10 +13,10 @@ namespace Icarus.Controllers.V1;
public class SongDataController : BaseController
{
#region Fields
private string _connectionString;
private ILogger<SongDataController> _logger;
private SongManager _songMgr;
private string _songTempDir;
private string? _connectionString;
private ILogger<SongDataController>? _logger;
private SongManager? _songMgr;
private string? _songTempDir;
#endregion
@@ -31,7 +31,7 @@ public class SongDataController : BaseController
_connectionString = _config.GetConnectionString("DefaultConnection");
_logger = logger;
_songTempDir = _config.GetValue<string>("TemporaryMusicPath");
_songMgr = new SongManager(config, _songTempDir);
_songMgr = new SongManager(config, _songTempDir!);
}
#endregion
@@ -39,13 +39,29 @@ public class SongDataController : BaseController
[HttpGet("download/{id}")]
public IActionResult Download(int id, [FromQuery] bool? randomizeFilename)
{
var songContext = new SongContext(_connectionString);
var songContext = new SongContext(_connectionString!);
var songMetaData = songContext.RetrieveRecord(new Song { Id = id});
var song = _songMgr.RetrieveSong(songMetaData).Result;
var filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.WAV_EXTENSION, songMetaData.Title, randomizeFilename);
var song = _songMgr!.RetrieveSong(songMetaData).Result;
string filename;
return File(song.Data, "application/x-msdownload", filename);
switch (songMetaData.AudioType)
{
case "wav":
filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.WAV_EXTENSION,
songMetaData.Title!, randomizeFilename);
break;
case "flac":
filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.FLAC_EXTENSION,
songMetaData.Title!, randomizeFilename);
break;
default:
filename = DirectoryManager.GenerateDownloadFilename(10, Constants.FileExtensions.DEFAULT_AUDIO_EXTENSION,
songMetaData.Title!, randomizeFilename);
break;
}
return File(song.Data!, "application/x-msdownload", filename);
}
// Assumes that the song already has metadata such as
@@ -65,20 +81,17 @@ public class SongDataController : BaseController
{
try
{
// Console.WriteLine("Uploading song...");
_logger.LogInformation("Uploading song...");
_logger!.LogInformation("Uploading song...");
var uploads = _songTempDir;
// Console.WriteLine($"Song Root Path {uploads}");
_logger.LogInformation($"Song root path {uploads}");
_logger!.LogInformation($"Song root path {uploads}");
foreach (var sng in songData)
if (sng.Length > 0)
{
// Console.WriteLine($"Song filename {sng.FileName}");
_logger.LogInformation($"Song filename {sng.FileName}");
_logger!.LogInformation($"Song filename {sng.FileName}");
_songMgr.SaveSongToFileSystem(sng).Wait();
_songMgr!.SaveSongToFileSystem(sng).Wait();
}
return Ok();
@@ -86,7 +99,7 @@ public class SongDataController : BaseController
catch (Exception ex)
{
var msg = ex.Message;
_logger.LogError(msg, "An error occurred");
_logger!.LogError(msg, "An error occurred");
}
return NotFound();
@@ -101,35 +114,67 @@ public class SongDataController : BaseController
{
try
{
if (up.SongData.Length > 0 && up.CoverArtData.Length > 0 && !string.IsNullOrEmpty(up.SongFile))
if (up.SongData!.Length > 0 && up.CoverArtData!.Length > 0 && !string.IsNullOrEmpty(up.SongFile))
{
var meta = new Utilities.MetadataRetriever();
var tmpSong = this._songMgr!.SaveSongTemp(up.SongData).Result;
if (!meta.IsSupportedFile(tmpSong.SongPath()) && !meta.IsSupportedFile(up.CoverArtData))
{
return BadRequest("Media is not supported");
}
else if (!meta.IsSupportedFile(tmpSong.SongPath()))
{
return BadRequest("Song is not supported");
}
else if (!meta.IsSupportedFile(up.CoverArtData))
{
return BadRequest("Cover art is not supported");
}
var song = Newtonsoft.Json.JsonConvert.DeserializeObject<Song>(up.SongFile);
var tokMgr = new TokenManager(this._config);
var tokMgr = new TokenManager(this._config!);
var accessToken = Request.Headers["Authorization"];
var userId = tokMgr.RetrieveUserIdFromToken(accessToken);
var userId = tokMgr.RetrieveUserIdFromToken(accessToken!);
if (userId != -1)
{
song.UserId = userId;
song!.UserId = userId;
}
_logger.LogInformation($"Song title: {song.Title}");
_logger!.LogInformation($"Song title: {song!.Title}");
_songMgr.SaveSongToFileSystem(up.SongData, up.CoverArtData, song);
song.Filename = tmpSong.Filename;
song.SongDirectory = tmpSong.SongDirectory;
song.DateCreated = tmpSong.DateCreated;
song.AudioType = meta.FileExtensionType(tmpSong.SongPath());
switch (song.AudioType)
{
case "wav":
song = _songMgr.SaveSongToFileSystem(up.SongData, up.CoverArtData, song);
break;
case "flac":
song = _songMgr.SaveFlacSongToFileSystem(up.SongData, up.CoverArtData, song);
break;
default:
return BadRequest();
}
return Ok(song);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message, "An error occurred");
_logger!.LogError(ex.Message, "An error occurred");
}
return Ok();
return BadRequest();
}
[HttpDelete("delete/{id}")]
public IActionResult DeleteSong(int id)
{
var songContext = new SongContext(_connectionString);
var songContext = new SongContext(_connectionString!);
var songMetaData = new Song{ Id = id };
Console.WriteLine($"Id {songMetaData.Id}");
@@ -138,14 +183,14 @@ public class SongDataController : BaseController
if (string.IsNullOrEmpty(songMetaData.Title))
{
_logger.LogInformation("Song does not exist");
_logger!.LogInformation("Song does not exist");
return NotFound("Song does not exist");
}
else
{
_logger.LogInformation("Starting process of deleting song from the filesystem and database");
_logger!.LogInformation("Starting process of deleting song from the filesystem and database");
_songMgr.DeleteSong(songMetaData);
_songMgr!.DeleteSong(songMetaData);
return Ok(songMetaData);
}
@@ -155,11 +200,11 @@ public class SongDataController : BaseController
public class UploadSongWithDataForm
{
[FromForm(Name = "file")]
public IFormFile SongData { get; set; }
public IFormFile? SongData { get; set; }
// NOTE: Think about making this optional and if it is not provided, use the stock cover art
[FromForm(Name = "cover")]
public IFormFile CoverArtData { get; set; }
public IFormFile? CoverArtData { get; set; }
[FromForm(Name = "metadata")]
public string SongFile { get; set; }
public string? SongFile { get; set; }
}
}
+5 -5
View File
@@ -11,7 +11,7 @@ namespace Icarus.Controllers.V1;
public class SongStreamController : BaseController
{
#region Fields
private ILogger<SongStreamController> _logger;
private ILogger<SongStreamController>? _logger;
#endregion
@@ -32,11 +32,11 @@ public class SongStreamController : BaseController
[HttpGet("{id}")]
public async Task<IActionResult> StreamSong(int id)
{
var context = new SongContext(_config.GetConnectionString("DefaultConnection"));
var context = new SongContext(_config!.GetConnectionString("DefaultConnection")!);
var song = context.Songs.FirstOrDefault(sng => sng.Id == id);
var song = context.Songs!.FirstOrDefault(sng => sng.Id == id);
var stream = new FileStream(song.SongPath(), FileMode.Open, FileAccess.Read);
var stream = new FileStream(song!.SongPath(), FileMode.Open, FileAccess.Read);
stream.Position = 0;
var filename = song.Filename;
@@ -45,7 +45,7 @@ public class SongStreamController : BaseController
filename = song.GenerateFilename();
}
_logger.LogInformation("Starting to stream song...>");
_logger!.LogInformation("Starting to stream song...>");
Console.WriteLine("Starting to streamsong...");
return await Task.Run(() => {