feat: add database connections, initial migration and early CLI tooling
All checks were successful
Update changelog / changelog (push) Successful in 25s

This commit is contained in:
2025-12-09 01:46:05 +01:00
parent ad3cd6710d
commit f648a73cb2
26 changed files with 2399 additions and 52 deletions

16
Entities/Album.cs Normal file
View File

@@ -0,0 +1,16 @@
namespace Shadow.Entities;
public class Album
{
required public int Id { get; set; }
public required string Name { get; set; }
public required string Uri { get; set; }
public int State { get; set; } = 0;
public int? ArtistId { get; set; } = null;
public Artist? Artist { get; set; } = null;
public List<Song> Songs { get; set; } = [];
public bool IsOk() => State == 0;
public bool IsOrphaned() => State == 1;
public bool IsArchived() => State == 2;
}

View File

@@ -0,0 +1,12 @@
namespace Shadow.Entities;
public class AlbumInteraction
{
required public int Id { get; set; }
required public int AlbumId { get; set; }
required public int UserId { get; set; }
public DateTime? PlayDate { get; set; } = null;
public bool Starred { get; set; } = false;
required public Album Album { get; set; }
required public User User { get; set; }
}

12
Entities/Artist.cs Normal file
View File

@@ -0,0 +1,12 @@
namespace Shadow.Entities;
public class Artist
{
required public int Id { get; set; }
required public string Name { get; set; }
required public string NormalizedName { get; set; }
// List of artists songs
public List<Song> Songs { get; set; } = [];
// List of artists albums
public List<Album> Albums { get; set; } = [];
}

10
Entities/Genre.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace Shadow.Entities;
public class Genre
{
required public int Id { get; set; }
required public string Name { get; set; }
required public string NormalizedName { get; set; }
// List of genre-song pairs of a given genre
public List<GenreSong> GenreSongPair { get; set; } = [];
}

10
Entities/GenreSong.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace Shadow.Entities;
public class GenreSong
{
// Composite keys
required public int GenreId { get; set; }
required public int SongId { get; set; }
required public Genre Genre { get; set; }
required public Song Song { get; set; }
}

14
Entities/Image.cs Normal file
View File

@@ -0,0 +1,14 @@
namespace Shadow.Entities;
public class Image
{
required public int Id { get; set; }
required public string Uri { get; set; }
public int State { get; set; }
public int? SongId { get; set; } = null;
public Song? Song { get; set; } = null;
public bool IsAvailable() => State == 0;
public bool IsOrphaned() => State == 1;
}

25
Entities/Playlist.cs Normal file
View File

@@ -0,0 +1,25 @@
namespace Shadow.Entities;
public class Playlist
{
required public int Id { get; set; }
required public string Name { get; set; }
required public string Uri { get; set; }
required public string Description { get; set; }
required public int CreatorId { get; set; } // UserId?
required public User Creator { get; set; }
public List<PlaylistUser> AuthorizedPlaylistUsers { get; set; } = [];
public bool CanAccess(User u) {
bool isUserPresent = false;
foreach (PlaylistUser pu in AuthorizedPlaylistUsers)
{
if (pu.User == u)
{
isUserPresent = true;
break;
}
}
return isUserPresent;
}
}

12
Entities/PlaylistSong.cs Normal file
View File

@@ -0,0 +1,12 @@
namespace Shadow.Entities;
public class PlaylistSong
{
// Composite keys
required public int PlaylistId { get; set; }
required public int SongId { get; set; }
required public int Index { get; set; }
required public Playlist Playlist { get; set; }
required public Song Song { get; set; }
}

9
Entities/PlaylistUser.cs Normal file
View File

@@ -0,0 +1,9 @@
namespace Shadow.Entities;
public class PlaylistUser
{
required public int PlaylistId { get; set; }
required public int UserId { get; set; }
required public Playlist Playlist { get; set; }
required public User User { get; set; }
}

12
Entities/Radio.cs Normal file
View File

@@ -0,0 +1,12 @@
namespace Shadow.Entities;
public class Radio
{
required public int Id { get; set; }
required public string Name { get; set; }
required public string NormalizedName { get; set; }
public string? Homepage { get; set; } = null;
required public string Url { get; set; }
required public int UserId { get; set; }
required public User User { get; set; }
}

59
Entities/Song.cs Normal file
View File

@@ -0,0 +1,59 @@
using System.Numerics;
namespace Shadow.Entities;
public class Song
{
public int Id { get; set; }
required public string Title { get; set; }
required public string Uri { get; set; }
required public string Filepath { get; set; }
public int State { get; set; }
required public string Filetype { get; set; }
public DateTime Date { get; set; }
public int Duration { get; set; }
public int Bitrate { get; set; }
public int Size { get; set; }
public string? Comment { get; set; } = null;
public int Channels { get; set; } = 2;
public int SamplingRate { get; set; }
public int? BitDepth { get; set; } = null;
public int Index { get; set; }
public int? TrackNumber { get; set; } = null;
public int? DiscNumber { get; set; } = null;
required public int AlbumId { get; set; }
required public int ArtistId { get; set; }
public int? ImageId { get; set; } = null;
// Songs without an album entry shall default to "[Unnamed album]".
required public Album Album { get; set; }
// Same for artists, with "[Unknown artist]".
required public Artist Artist { get; set; }
public List<GenreSong> GenreSongPair { get; set; } = [];
public Image? Image { get; set; } = null;
public bool IsOk() => State == 0;
public bool IsOrphaned() => State == 1;
public bool IsArchived() => State == 2;
public string GetMimeType()
{
// Might be nice to rewrite like this later:
// https://stackoverflow.com/a/47601452
string mimeType = String.Empty;
switch (Filetype)
{
case "flac":
mimeType = "audio/flac"; break;
case "m4a":
mimeType = "audio/mp4"; break;
case "mp3":
mimeType = "audio/mpeg"; break;
case "ogg":
mimeType = "audio/ogg"; break;
case "wav":
mimeType = "audio/x-wav"; break;
default:
mimeType = "audio/unknown"; break;
}
return mimeType;
}
}

View File

@@ -0,0 +1,15 @@
namespace Shadow.Entities;
public class SongInteraction
{
public int Id { get; set; }
required public int SongId { get; set; }
required public int UserId { get; set; }
DateTime? PlayDate { get; set; } = null;
public int PlayCount { get; set; } = 0;
public bool? Starred { get; set; } = false;
public int Rating { get; set; } = 0;
public Song? Song { get; set; }
public User? User { get; set; }
}

18
Entities/User.cs Normal file
View File

@@ -0,0 +1,18 @@
namespace Shadow.Entities;
public class User
{
public int Id { get; set; }
required public string Name { get; set; }
required public string NormalizedName { get; set; }
// required public string Email { get; set; } // Currently not used
required public string Password { get; set; }
public int Role { get; set; } = 1;
public List<Playlist> Playlists { get; set; } = [];
public List<AlbumInteraction> AlbumInteractions { get; set; } = [];
public List<SongInteraction> SongInteractions { get; set; } = [];
public bool IsAdmin() => Role == 0;
public bool IsUnpriviledgedUser() => Role == 1;
}