updated to v1.1.0; resolves #35

This commit is contained in:
Steven Hildreth 2019-11-28 11:38:26 -06:00
parent 15c0c04504
commit 6fc03268b6
142 changed files with 3063 additions and 1501 deletions

View file

@ -25,7 +25,6 @@
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View file

@ -18,7 +18,7 @@ namespace Roadie.Library.Tests
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var token = ServiceBase.TrackPlayToken(new ApplicationUser var token = ServiceBase.TrackPlayToken(new User
{ {
Id = 1, Id = 1,
CreatedDate = DateTime.UtcNow CreatedDate = DateTime.UtcNow

View file

@ -1,12 +1,10 @@
using System; namespace Roadie.Library.Configuration
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Configuration
{ {
public enum DbContexts : short public enum DbContexts : short
{ {
MySQL = 1, Unknown = 0,
File = 2 SQLite = 1,
File = 2,
MySQL = 3
} }
} }

View file

@ -19,7 +19,7 @@ namespace Roadie.Library.Configuration
{ {
DatabaseFormat = FileDatabaseFormat.BSON; DatabaseFormat = FileDatabaseFormat.BSON;
DatabaseName = "roadie"; DatabaseName = "roadie";
DatabaseFolder = @"M:\db"; DatabaseFolder = "data/db";
} }
} }
} }

View file

@ -19,8 +19,7 @@ namespace Roadie.Library.Configuration
var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey"); var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey");
if (keySetting != null) if (keySetting != null)
return keySetting.Key; return keySetting.Key;
Trace.WriteLine( Trace.WriteLine("Unable To Find Api Key with Key Name of 'DiscogsConsumerKey', Discogs Integration Disabled", "Warning");
"Unable To Find Api Key with Key Name of 'DiscogsConsumerKey', Discogs Integration Disabled");
return null; return null;
} }
} }
@ -32,8 +31,7 @@ namespace Roadie.Library.Configuration
var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey"); var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey");
if (keySetting != null) if (keySetting != null)
return keySetting.KeySecret; return keySetting.KeySecret;
Trace.WriteLine( Trace.WriteLine("Unable To Find Api Key with Key Name of 'DiscogsConsumerKey', Discogs Integration Disabled", "Warning");
"Unable To Find Api Key with Key Name of 'DiscogsConsumerKey', Discogs Integration Disabled");
return null; return null;
} }
} }
@ -77,7 +75,7 @@ namespace Roadie.Library.Configuration
var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey"); var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey");
if (keySetting != null) if (keySetting != null)
return keySetting.Key; return keySetting.Key;
Trace.WriteLine("Unable To Find Api Key with Key Name of 'LastFMApiKey', Last FM Integration Disabled"); Trace.WriteLine("Unable To Find Api Key with Key Name of 'LastFMApiKey', Last FM Integration Disabled", "Warning");
return null; return null;
} }
} }
@ -89,7 +87,7 @@ namespace Roadie.Library.Configuration
var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey"); var keySetting = ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey");
if (keySetting != null) if (keySetting != null)
return keySetting.KeySecret; return keySetting.KeySecret;
Trace.WriteLine("Unable To Find Api Key with Key Name of 'LastFMApiKey', Last FM Integration Disabled"); Trace.WriteLine("Unable To Find Api Key with Key Name of 'LastFMApiKey', Last FM Integration Disabled", "Warning");
return null; return null;
} }
} }

View file

@ -9,7 +9,7 @@ namespace Roadie.Library.Configuration
{ {
public static string RoadieImageFolder = "__roadie_images"; public static string RoadieImageFolder = "__roadie_images";
public DbContexts DbContextToUse { get; set; } public DbContexts DbContextToUse { get; set; } = DbContexts.SQLite;
/// <summary> /// <summary>
/// If the artist name is found in the values then use the key. /// If the artist name is found in the values then use the key.
@ -81,7 +81,6 @@ namespace Roadie.Library.Configuration
} }
public ImageSize LargeImageSize { get; set; } public ImageSize LargeImageSize { get; set; }
public string LibraryFolder { get; set; } public string LibraryFolder { get; set; }
public string ListenAddress { get; set; } public string ListenAddress { get; set; }
@ -151,7 +150,7 @@ namespace Roadie.Library.Configuration
public RoadieSettings() public RoadieSettings()
{ {
DbContextToUse = DbContexts.MySQL; DbContextToUse = DbContexts.SQLite;
ArtistNameReplace = new Dictionary<string, IEnumerable<string>> ArtistNameReplace = new Dictionary<string, IEnumerable<string>>
{ {
{ "AC/DC", new List<string>{ "AC; DC", "AC;DC", "AC/ DC", "AC DC" }}, { "AC/DC", new List<string>{ "AC; DC", "AC;DC", "AC/ DC", "AC DC" }},
@ -159,10 +158,10 @@ namespace Roadie.Library.Configuration
}; };
DefaultTimeZone = "US / Central"; DefaultTimeZone = "US / Central";
DontDoMetaDataProvidersSearchArtists = new List<string> { "Various Artists", "Sound Tracks" }; DontDoMetaDataProvidersSearchArtists = new List<string> { "Various Artists", "Sound Tracks" };
FileExtensionsToDelete = new List<string> { ".accurip", ".bmp", ".cue", ".dat", ".db", ".exe", ".gif", ".htm", ".html", ".ini", ".log", ".jpg", ".jpeg", ".par", ".par2", ".pdf", ".png", ".md5", ".mht", ".mpg", ".m3u", ".nfo", ".nzb", ".pls", ".sfv", ".srr", ".txt", ".url" }; FileExtensionsToDelete = new List<string> { ".accurip", ".cue", ".dat", ".db", ".exe", ".htm", ".html", ".ini", ".log", ".par", ".par2", ".pdf", ".md5", ".mht", ".mpg", ".m3u", ".nfo", ".nzb", ".pls", ".sfv", ".srr", ".txt", ".url" };
InboundFolder = "M:/inbound"; InboundFolder = "data/inbound";
LargeImageSize = new ImageSize { Width = 500, Height = 500 }; LargeImageSize = new ImageSize { Width = 500, Height = 500 };
LibraryFolder = "M:/library"; LibraryFolder = "data/library";
MaximumImageSize = new ImageSize { Width = 2048, Height = 2048 }; MaximumImageSize = new ImageSize { Width = 2048, Height = 2048 };
MediumImageSize = new ImageSize { Width = 320, Height = 320 }; MediumImageSize = new ImageSize { Width = 320, Height = 320 };
RecordNoResultSearches = true; RecordNoResultSearches = true;
@ -174,7 +173,6 @@ namespace Roadie.Library.Configuration
SmtpFromAddress = "noreply@roadie.rocks"; SmtpFromAddress = "noreply@roadie.rocks";
SmtpPort = 587; SmtpPort = 587;
SmtpUsername = "roadie"; SmtpUsername = "roadie";
SmtpHost = "smtp.roadie.rocks";
SmtpUseSSl = true; SmtpUseSSl = true;
Inspector = new Inspector(); Inspector = new Inspector();

View file

@ -1,5 +1,4 @@
using Roadie.Library.Enums; using Roadie.Library.Enums;
using Roadie.Library.Imaging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@ -10,12 +9,18 @@ namespace Roadie.Library.Data
[Table("artist")] [Table("artist")]
public partial class Artist : BeginAndEndNamedEntityBase public partial class Artist : BeginAndEndNamedEntityBase
{ {
[Column("amgId")] [MaxLength(100)] public string AmgId { get; set; } [Column("amgId")]
[MaxLength(100)]
public string AmgId { get; set; }
[Column("artistType", TypeName = "enum")] [Column("artistType", TypeName = "enum")]
public string ArtistType { get; set; } public string ArtistType { get; set; }
[InverseProperty("Artist")] public ICollection<ArtistAssociation> AssociatedArtists { get; set; } /// <summary>
/// Artists who this Artist is Associated To
/// </summary>
[InverseProperty("Artist")]
public virtual ICollection<ArtistAssociation> AssociatedArtists { get; set; }
[Column("bandStatus", TypeName = "enum")] [Column("bandStatus", TypeName = "enum")]
public BandStatus? BandStatus { get; set; } public BandStatus? BandStatus { get; set; }
@ -27,46 +32,77 @@ namespace Roadie.Library.Data
[Column("birthDate", TypeName = "date")] [Column("birthDate", TypeName = "date")]
public DateTime? BirthDate { get; set; } public DateTime? BirthDate { get; set; }
public ICollection<Comment> Comments { get; set; } [InverseProperty("Artist")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("discogsId")] [MaxLength(50)] public string DiscogsId { get; set; } [InverseProperty("Artist")]
public virtual ICollection<Credit> Credits { get; set; }
public ICollection<ArtistGenre> Genres { get; set; } [Column("discogsId")]
[MaxLength(50)]
public string DiscogsId { get; set; }
[InverseProperty("Artist")]
public virtual ICollection<ArtistGenre> Genres { get; set; }
/// <summary>
/// Where the Artist is the Artist on the Track not on an Artist Release
/// </summary>
[InverseProperty("TrackArtist")]
public virtual ICollection<Track> Tracks { get; set; }
[Column("isniList", TypeName = "text")] [Column("isniList", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string ISNI { get; set; } public string ISNI { get; set; }
[Column("iTunesId")] [MaxLength(100)] public string ITunesId { get; set; } [Column("iTunesId")]
[MaxLength(100)]
public string ITunesId { get; set; }
[Column("lastPlayed")] public DateTime? LastPlayed { get; set; } [Column("lastPlayed")]
public DateTime? LastPlayed { get; set; }
[Column("musicBrainzId")] [Column("musicBrainzId")]
[MaxLength(100)] [MaxLength(100)]
public string MusicBrainzId { get; set; } public string MusicBrainzId { get; set; }
[Column("playedCount")] public int? PlayedCount { get; set; } [Column("playedCount")]
public int? PlayedCount { get; set; }
[Column("profile", TypeName = "text")] [Column("profile", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string Profile { get; set; } public string Profile { get; set; }
[Column("rank")] public decimal? Rank { get; set; } [Column("rank")]
public decimal? Rank { get; set; }
[Column("rating")] public short? Rating { get; set; } [Column("rating")]
public short? Rating { get; set; }
[Column("realName")] [MaxLength(500)] public string RealName { get; set; } [Column("realName")]
[MaxLength(500)]
public string RealName { get; set; }
[Column("releaseCount")] public int? ReleaseCount { get; set; } [Column("releaseCount")]
public int? ReleaseCount { get; set; }
//public List<Release> Releases { get; set; } public virtual ICollection<Release> Releases { get; set; }
public ICollection<Release> Releases { get; set; }
[InverseProperty("Artist")] public ICollection<ArtistSimilar> SimilarArtists { get; set; } /// <summary>
/// Artists who are similiar to this Artist
/// </summary>
[InverseProperty("Artist")]
public virtual ICollection<ArtistSimilar> SimilarArtists { get; set; }
[Column("spotifyId")] [MaxLength(100)] public string SpotifyId { get; set; } [Column("spotifyId")]
[MaxLength(100)]
public string SpotifyId { get; set; }
[Column("trackCount")] public int? TrackCount { get; set; } [Column("trackCount")]
public int? TrackCount { get; set; }
[InverseProperty("Artist")]
public virtual ICollection<UserArtist> UserArtists { get; set; }
public Artist() public Artist()
{ {
@ -75,6 +111,8 @@ namespace Roadie.Library.Data
AssociatedArtists = new HashSet<ArtistAssociation>(); AssociatedArtists = new HashSet<ArtistAssociation>();
SimilarArtists = new HashSet<ArtistSimilar>(); SimilarArtists = new HashSet<ArtistSimilar>();
Comments = new HashSet<Comment>(); Comments = new HashSet<Comment>();
UserArtists = new HashSet<UserArtist>();
Rating = 0; Rating = 0;
Status = Statuses.Ok; Status = Statuses.Ok;
} }

View file

@ -6,15 +6,20 @@ namespace Roadie.Library.Data
[Table("artistAssociation")] [Table("artistAssociation")]
public class ArtistAssociation public class ArtistAssociation
{ {
//[ForeignKey("artistId")] [ForeignKey(nameof(ArtistId))]
public Artist Artist { get; set; } [InverseProperty("AssociatedArtists")]
public virtual Artist Artist { get; set; }
[Column("artistId")] [Required] public int ArtistId { get; set; } [Column("artistId")]
[Required]
public int ArtistId { get; set; }
//[ForeignKey("associatedArtistId")] [ForeignKey(nameof(AssociatedArtistId))]
public Artist AssociatedArtist { get; set; } public virtual Artist AssociatedArtist { get; set; }
[Column("associatedArtistId")] public int AssociatedArtistId { get; set; } [Column("associatedArtistId")]
[Required]
public int AssociatedArtistId { get; set; }
[Key] [Key]
[Column("id")] [Column("id")]

View file

@ -6,17 +6,25 @@ namespace Roadie.Library.Data
[Table("artistGenreTable")] [Table("artistGenreTable")]
public class ArtistGenre public class ArtistGenre
{ {
public Artist Artist { get; set; }
[Column("artistId")] [Required] public int ArtistId { get; set; }
public Genre Genre { get; set; }
[Column("genreId")] [Required] public int? GenreId { get; set; }
[Column("id")]
[Key] [Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; } public int Id { get; set; }
[Column("artistId")]
[Required]
public int ArtistId { get; set; }
[Column("genreId")]
[Required]
public int GenreId { get; set; }
[ForeignKey(nameof(ArtistId))]
[InverseProperty("Genres")]
public virtual Artist Artist { get; set; }
[ForeignKey(nameof(GenreId))]
[InverseProperty("Artists")]
public virtual Genre Genre { get; set; }
} }
} }

View file

@ -3,8 +3,6 @@ using Roadie.Library.Utility;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Security.Cryptography;
namespace Roadie.Library.Data namespace Roadie.Library.Data
{ {
@ -29,8 +27,6 @@ namespace Roadie.Library.Data
public bool IsValid => !string.IsNullOrEmpty(Name); public bool IsValid => !string.IsNullOrEmpty(Name);
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
public string GroupBy => SortNameValue.Substring(0, 1).ToUpper(); public string GroupBy => SortNameValue.Substring(0, 1).ToUpper();
public static string CacheRegionUrn(Guid Id) public static string CacheRegionUrn(Guid Id)
@ -55,7 +51,7 @@ namespace Roadie.Library.Data
public override string ToString() public override string ToString()
{ {
return $"Id [{ Id }], Status [{ Status }], Name [{ Name }], SortName [{ SortNameValue}], RoadieId [{ RoadieId}]"; return $"Id [{ Id }], Status [{ Status }], Name [{ Name }], SortName [{ SortName }], RoadieId [{ RoadieId}]";
} }
} }
} }

View file

@ -6,19 +6,23 @@ namespace Roadie.Library.Data
[Table("artistSimilar")] [Table("artistSimilar")]
public class ArtistSimilar public class ArtistSimilar
{ {
//[ForeignKey("artistId")] [ForeignKey(nameof(ArtistId))]
public Artist Artist { get; set; } [InverseProperty("SimilarArtists")]
public virtual Artist Artist { get; set; }
[Column("artistId")] [Required] public int ArtistId { get; set; } [Column("artistId")]
[Required]
public int ArtistId { get; set; }
[Key] [Key]
[Column("id")] [Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; } public int Id { get; set; }
//[ForeignKey("associatedArtistId")] [ForeignKey(nameof(SimilarArtistId))]
public Artist SimilarArtist { get; set; } public virtual Artist SimilarArtist { get; set; }
[Column("similarArtistId")] public int SimilarArtistId { get; set; } [Column("similarArtistId")]
public int SimilarArtistId { get; set; }
} }
} }

View file

@ -8,6 +8,7 @@ namespace Roadie.Library.Data
[Column("beginDate", TypeName = "date")] [Column("beginDate", TypeName = "date")]
public DateTime? BeginDate { get; set; } public DateTime? BeginDate { get; set; }
[Column("endDate", TypeName = "date")] public DateTime? EndDate { get; set; } [Column("endDate", TypeName = "date")]
public DateTime? EndDate { get; set; }
} }
} }

View file

@ -5,8 +5,10 @@ namespace Roadie.Library.Data
{ {
public abstract class BeginAndEndNamedEntityBase : NamedEntityBase public abstract class BeginAndEndNamedEntityBase : NamedEntityBase
{ {
[Column("beginDate")] public DateTime? BeginDate { get; set; } [Column("beginDate")]
public DateTime? BeginDate { get; set; }
[Column("endDate")] public DateTime? EndDate { get; set; } [Column("endDate")]
public DateTime? EndDate { get; set; }
} }
} }

View file

@ -8,16 +8,27 @@ namespace Roadie.Library.Data
[Table("bookmark")] [Table("bookmark")]
public partial class Bookmark : EntityBase public partial class Bookmark : EntityBase
{ {
[Column("bookmarkTargetId")] public int BookmarkTargetId { get; set; } [Column("bookmarkTargetId")]
public int BookmarkTargetId { get; set; }
// public short? Type { get; set; } // public short? Type { get; set; }
[Column("bookmarkType")] public BookmarkType? BookmarkType { get; set; } [Column("bookmarkType")]
public BookmarkType? BookmarkType { get; set; }
[Column("Comment")] [MaxLength(4000)] public string Comment { get; set; } [Column("Comment")]
[Column("position")] public int? Position { get; set; } [MaxLength(4000)]
public ApplicationUser User { get; set; } public string Comment { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("position")]
public int? Position { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("Bookmarks")]
public virtual User User { get; set; }
[Column("userId")]
[Required]
public int UserId { get; set; }
} }
} }

View file

@ -12,8 +12,12 @@ namespace Roadie.Library.Data
[MaxLength(5000)] [MaxLength(5000)]
public string Message { get; set; } public string Message { get; set; }
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("ChatMessages")]
public virtual User User { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("userId")]
[Required]
public int UserId { get; set; }
} }
} }

View file

@ -1,4 +1,5 @@
using Roadie.Library.Enums; using Roadie.Library.Enums;
using Roadie.Library.Identity;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -11,17 +12,18 @@ namespace Roadie.Library.Data
[Column("collectionCount")] [Column("collectionCount")]
public int CollectionCount { get; set; } public int CollectionCount { get; set; }
[Column("collectionType")] [Column("collectionType")]
public CollectionType? CollectionType { get; set; } public CollectionType? CollectionType { get; set; }
public ICollection<Comment> Comments { get; set; } [InverseProperty("Collection")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("description")] [Column("description")]
[MaxLength(4000)] [MaxLength(4000)]
public string Description { get; set; } public string Description { get; set; }
[Column("edition")] [Column("edition")]
[MaxLength(200)] [MaxLength(200)]
public string Edition { get; set; } public string Edition { get; set; }
[Column("listInCSV", TypeName = "text")] [Column("listInCSV", TypeName = "text")]
@ -32,9 +34,17 @@ namespace Roadie.Library.Data
[MaxLength(200)] [MaxLength(200)]
public string ListInCSVFormat { get; set; } public string ListInCSVFormat { get; set; }
[Column("maintainerId")] [Column("maintainerId")]
public int MaintainerId { get; set; } public int MaintainerId { get; set; }
public ICollection<CollectionRelease> Releases { get; set; } [ForeignKey(nameof(MaintainerId))]
[InverseProperty(nameof(User.Collections))]
public virtual User Maintainer { get; set; }
[InverseProperty("Collection")]
public virtual ICollection<CollectionRelease> Releases { get; set; }
[InverseProperty("Collection")]
public virtual ICollection<CollectionMissing> MissingReleases { get; set; }
} }
} }

View file

@ -6,18 +6,30 @@ namespace Roadie.Library.Data
[Table("collectionMissing")] [Table("collectionMissing")]
public class CollectionMissing public class CollectionMissing
{ {
[Column("artist")] [MaxLength(1000)] public string Artist { get; set; } [Column("artist")]
[MaxLength(1000)]
public string Artist { get; set; }
[Column("collectionId")] public int CollectionId { get; set; } [Column("collectionId")]
public int CollectionId { get; set; }
[ForeignKey(nameof(CollectionId))]
[InverseProperty("MissingReleases")]
public virtual Collection Collection { get; set; }
[Column("id")] [Column("id")]
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; } public int Id { get; set; }
[Column("isArtistFound")] public bool IsArtistFound { get; set; } [Column("isArtistFound")]
public bool IsArtistFound { get; set; }
[Column("position")] public int Position { get; set; } [Column("position")]
[Column("release")] [MaxLength(1000)] public string Release { get; set; } public int Position { get; set; }
[Column("release")]
[MaxLength(1000)]
public string Release { get; set; }
} }
} }

View file

@ -98,7 +98,9 @@ namespace Roadie.Library.Data
public Collection() public Collection()
{ {
Releases = new HashSet<CollectionRelease>(); Releases = new HashSet<CollectionRelease>();
MissingReleases = new HashSet<CollectionMissing>();
Comments = new HashSet<Comment>(); Comments = new HashSet<Comment>();
ListInCSVFormat = "Position,Release,Artist"; ListInCSVFormat = "Position,Release,Artist";
CollectionType = Enums.CollectionType.Rank; CollectionType = Enums.CollectionType.Rank;
} }
@ -128,19 +130,21 @@ namespace Roadie.Library.Data
}; };
configuration.BadDataFound = context => configuration.BadDataFound = context =>
{ {
Trace.WriteLine($"PositionArtistReleases: Bad data found on row '{context.RawRow}'"); Trace.WriteLine($"PositionArtistReleases: Bad data found on row '{context.RawRow}'", "Warning");
}; };
var csv = new CsvReader(sr, configuration); using (var csv = new CsvReader(sr, configuration))
while (csv.Read())
{ {
index++; while (csv.Read())
rows.Add(new PositionArtistRelease
{ {
Index = index, index++;
Position = csv.GetField<int>(PositionColumn), rows.Add(new PositionArtistRelease
Artist = SafeParser.ToString(csv.GetField<string>(ArtistColumn)), {
Release = SafeParser.ToString(csv.GetField<string>(ReleaseColumn)) Index = index,
}); Position = csv.GetField<int>(PositionColumn),
Artist = SafeParser.ToString(csv.GetField<string>(ArtistColumn)),
Release = SafeParser.ToString(csv.GetField<string>(ReleaseColumn))
});
}
} }
} }
@ -156,30 +160,5 @@ namespace Roadie.Library.Data
} }
} }
[Serializable]
public class PositionArtistRelease
{
public string Artist { get; set; }
/// <summary>
/// This is the index (position in the list regardless of the position number)
/// </summary>
[JsonIgnore]
public int Index { get; set; }
/// <summary>
/// This is the position number for the list (can be a year "1984" can be a number "14")
/// </summary>
public int Position { get; set; }
public string Release { get; set; }
[JsonIgnore] public Statuses Status { get; set; }
[JsonProperty("Status")] public string StatusVerbose => Status.ToString();
public override string ToString()
{
return string.Format("Position [{0}], Artist [{1}], Release [{2}]", Position, Artist, Release);
}
}
} }

View file

@ -6,14 +6,23 @@ namespace Roadie.Library.Data
[Table("collectionrelease")] [Table("collectionrelease")]
public class CollectionRelease : EntityBase public class CollectionRelease : EntityBase
{ {
public Collection Collection { get; set; } [ForeignKey(nameof(CollectionId))]
[InverseProperty("Releases")]
public virtual Collection Collection { get; set; }
[Column("collectionId")] [Required] public int CollectionId { get; set; } [Column("collectionId")]
[Required]
public int CollectionId { get; set; }
[Column("listNumber")] public int ListNumber { get; set; } [Column("listNumber")]
public int ListNumber { get; set; }
public Release Release { get; set; } [ForeignKey(nameof(ReleaseId))]
[InverseProperty("Collections")]
public virtual Release Release { get; set; }
[Column("releaseId")] [Required] public int ReleaseId { get; set; } [Column("releaseId")]
[Required]
public int ReleaseId { get; set; }
} }
} }

View file

@ -9,34 +9,76 @@ namespace Roadie.Library.Data
[Table("comment")] [Table("comment")]
public partial class Comment : EntityBase public partial class Comment : EntityBase
{ {
[Column("artistId")] public int? ArtistId { get; set; } [Column("artistId")]
public int? ArtistId { get; set; }
[ForeignKey(nameof(ArtistId))]
[InverseProperty("Comments")]
public virtual Artist Artist { get; set; }
[Column("comment")] [Column("comment")]
[MaxLength(25500)] [MaxLength(25500)]
[Required] [Required]
public string Cmt { get; set; } public string Cmt { get; set; }
[Column("collectionId")] public int? CollectionId { get; set; } [Column("collectionId")]
public int? CollectionId { get; set; }
[Column("genreId")] public int? GenreId { get; set; } [ForeignKey(nameof(CollectionId))]
[InverseProperty("Comments")]
public virtual Collection Collection { get; set; }
[NotMapped] public new bool? IsLocked { get; set; } [Column("genreId")]
public int? GenreId { get; set; }
[Column("labelId")] public int? LabelId { get; set; } [ForeignKey(nameof(GenreId))]
[InverseProperty("Comments")]
public virtual Genre Genre { get; set; }
[Column("playlistId")] public int? PlaylistId { get; set; } [NotMapped]
public new bool? IsLocked { get; set; }
public ICollection<CommentReaction> Reactions { get; set; } [Column("labelId")]
public int? LabelId { get; set; }
[Column("releaseId")] public int? ReleaseId { get; set; } [ForeignKey(nameof(LabelId))]
[InverseProperty("Comments")]
public virtual Label Label { get; set; }
[Column("replyToCommentId")] public int? ReplyToCommentId { get; set; } [Column("playlistId")]
public int? PlaylistId { get; set; }
[Column("trackId")] public int? TrackId { get; set; } [ForeignKey(nameof(PlaylistId))]
[InverseProperty("Comments")]
public virtual Playlist Playlist { get; set; }
public ApplicationUser User { get; set; } [InverseProperty("Comment")]
public virtual ICollection<CommentReaction> Reactions { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("releaseId")]
public int? ReleaseId { get; set; }
[ForeignKey(nameof(ReleaseId))]
[InverseProperty("Comments")]
public virtual Release Release { get; set; }
[Column("replyToCommentId")]
public int? ReplyToCommentId { get; set; }
[Column("trackId")]
public int? TrackId { get; set; }
[ForeignKey(nameof(TrackId))]
[InverseProperty("Comments")]
public virtual Track Track { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("Comments")]
public virtual User User { get; set; }
[Column("userId")]
[Required]
public int UserId { get; set; }
public Comment() public Comment()
{ {

View file

@ -10,14 +10,25 @@ namespace Roadie.Library.Data
{ {
public Comment Comment { get; set; } public Comment Comment { get; set; }
[Column("commentId")] [Required] public int CommentId { get; set; } [Column("commentId")]
[NotMapped] public new bool? IsLocked { get; set; } [Required]
[Column("reaction")] public string Reaction { get; set; } public int CommentId { get; set; }
[NotMapped]
public new bool? IsLocked { get; set; }
[Column("reaction")]
public string Reaction { get; set; }
[NotMapped] [NotMapped]
public Enums.CommentReaction ReactionValue => SafeParser.ToEnum<Enums.CommentReaction>(Reaction ?? "Unknown"); public Enums.CommentReaction ReactionValue => SafeParser.ToEnum<Enums.CommentReaction>(Reaction ?? "Unknown");
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[Column("userId")] [Required] public int UserId { get; set; } [InverseProperty("CommentReactions")]
public virtual User User { get; set; }
[Column("userId")]
[Required]
public int UserId { get; set; }
} }
} }

View file

@ -1,9 +1,9 @@
using FileContextCore; using FileContextCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
using Roadie.Library.Data.Context.Implementation; using Roadie.Library.Data.Context.Implementation;
using System; using System;
using System.IO;
namespace Roadie.Library.Data.Context namespace Roadie.Library.Data.Context
{ {
@ -13,6 +13,12 @@ namespace Roadie.Library.Data.Context
{ {
switch (configuration.DbContextToUse) switch (configuration.DbContextToUse)
{ {
case DbContexts.SQLite:
var sqlLiteOptionsBuilder = new DbContextOptionsBuilder<SQLiteRoadieDbContext>();
var databaseName = Path.Combine(configuration.FileDatabaseOptions.DatabaseFolder, $"{ configuration.FileDatabaseOptions.DatabaseName }.db");
sqlLiteOptionsBuilder.UseSqlite($"Filename={databaseName}");
return new SQLiteRoadieDbContext(sqlLiteOptionsBuilder.Options);
case DbContexts.File: case DbContexts.File:
var fileOptionsBuilder = new DbContextOptionsBuilder<MySQLRoadieDbContext>(); var fileOptionsBuilder = new DbContextOptionsBuilder<MySQLRoadieDbContext>();
fileOptionsBuilder.UseFileContextDatabase(configuration.FileDatabaseOptions.DatabaseFormat.ToString().ToLower(), fileOptionsBuilder.UseFileContextDatabase(configuration.FileDatabaseOptions.DatabaseFormat.ToString().ToLower(),
@ -20,17 +26,19 @@ namespace Roadie.Library.Data.Context
location: configuration.FileDatabaseOptions.DatabaseFolder); location: configuration.FileDatabaseOptions.DatabaseFolder);
return new FileRoadieDbContext(fileOptionsBuilder.Options); return new FileRoadieDbContext(fileOptionsBuilder.Options);
default: case DbContexts.MySQL:
var mysqlOptionsBuilder = new DbContextOptionsBuilder<MySQLRoadieDbContext>(); var mysqlOptionsBuilder = new DbContextOptionsBuilder<MySQLRoadieDbContext>();
mysqlOptionsBuilder.UseMySql(configuration.ConnectionString, mySqlOptions => mysqlOptionsBuilder.UseMySql(configuration.ConnectionString, mySqlOptions =>
{ {
mySqlOptions.ServerVersion(new Version(5, 5), ServerType.MariaDb);
mySqlOptions.EnableRetryOnFailure( mySqlOptions.EnableRetryOnFailure(
10, 10,
TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30),
null); null);
}); });
return new MySQLRoadieDbContext(mysqlOptionsBuilder.Options); return new MySQLRoadieDbContext(mysqlOptionsBuilder.Options);
default:
throw new NotImplementedException("Unknown DbContext Type");
} }
} }
} }

View file

@ -42,8 +42,8 @@ namespace Roadie.Library.Data.Context
DbSet<UserArtist> UserArtists { get; set; } DbSet<UserArtist> UserArtists { get; set; }
DbSet<UserQue> UserQues { get; set; } DbSet<UserQue> UserQues { get; set; }
DbSet<UserRelease> UserReleases { get; set; } DbSet<UserRelease> UserReleases { get; set; }
DbSet<ApplicationRole> UserRoles { get; set; } DbSet<UserRole> UserRoles { get; set; }
DbSet<ApplicationUser> Users { get; set; } DbSet<User> Users { get; set; }
DbSet<UserTrack> UserTracks { get; set; } DbSet<UserTrack> UserTracks { get; set; }
DbSet<InviteToken> InviteTokens { get; set; } DbSet<InviteToken> InviteTokens { get; set; }

View file

@ -169,11 +169,11 @@ namespace Roadie.Library.Data.Context.Implementation
} }
else else
{ {
randomReleases = await(from r in Releases randomReleases = await (from r in Releases
join ur in UserReleases on r.Id equals ur.ReleaseId into urg join ur in UserReleases on r.Id equals ur.ReleaseId into urg
from ur in urg.DefaultIfEmpty() from ur in urg.DefaultIfEmpty()
where (ur == null || (ur.UserId == userId && ur.IsDisliked == false)) where (ur == null || (ur.UserId == userId && ur.IsDisliked == false))
select r) select r)
.OrderBy(x => Guid.NewGuid()) .OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync();

View file

@ -188,7 +188,7 @@ namespace Roadie.Library.Data.Context.Implementation
WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0) WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0)
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Tracks.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }

View file

@ -0,0 +1,111 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Roadie.Library.Data.Context.Implementation
{
/// <summary>
/// SQLite implementation of DbContext
/// </summary>
public sealed class SQLiteRoadieDbContext : LinqDbContextBase
{
private static bool _created = false;
public SQLiteRoadieDbContext(DbContextOptions options)
: base(options)
{
if (!_created)
{
_created = true;
Database.EnsureCreated();
}
}
public override async Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
// TODO Rating?
var sql = @"SELECT a.id
FROM artist a
WHERE(a.id NOT IN(select artistId FROM userartist where userId = {1} and isDisliked = 1))
OR(a.id IN (select artistId FROM userartist where userId = {1} and isFavorite = 1)
AND {2} = 1)
order by random()
LIMIT 0, {0}";
var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override async Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT g.id
FROM genre g
order by random()
LIMIT 0, {0}";
var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override async Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT l.id
FROM label l
order by random()
LIMIT 0, {0}";
var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override async Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
// TODO Rating?
var sql = @"SELECT r.id
FROM release r
WHERE (r.id NOT IN (select releaseId FROM userrelease where userId = {1} and isDisliked = 1))
OR (r.id IN (select releaseId FROM userrelease where userId = {1} and isFavorite = 1)
AND {2} = 1)
order by random()
LIMIT 0, {0}";
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override async Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
// When using the regular 'FromSqlRaw' with parameters SQLite returns no records.
var df = doOnlyFavorites ? "1" : "0";
var dr = doOnlyRated ? "1" : "0";
var sql = @$"SELECT t.id
FROM track t
WHERE ((t.rating > 0 AND {dr} = 1) OR {dr} = 0)
AND ((t.id NOT IN (select tt.id
FROM track tt
JOIN releasemedia rm on (tt.releaseMediaId = rm.id)
JOIN userartist ua on (rm.id = ua.artistId)
WHERE ua.userId = {userId} AND ua.isDisliked = 1))
AND (t.id NOT IN (select tt.id
FROM track tt
JOIN releasemedia rm on (tt.releaseMediaId = rm.id)
JOIN userrelease ur on (rm.releaseId = ur.releaseId)
WHERE ur.userId = {userId} AND ur.isDisliked = 1))
AND (t.id NOT IN (select tt.id
FROM track tt
JOIN usertrack ut on (tt.id = ut.trackId)
WHERE ut.userId = {userId} AND ut.isDisliked = 1)))
AND ((t.id IN (select tt.id
FROM track tt
JOIN usertrack ut on (tt.id = ut.trackId)
WHERE ut.userId = {userId} AND ut.isFavorite = 1) AND {df} = 1) OR {df} = 0)
order by random()
LIMIT 0, {randomLimit}";
var ids = await Tracks.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
}
}

View file

@ -65,9 +65,9 @@ namespace Roadie.Library.Data.Context
public DbSet<UserRelease> UserReleases { get; set; } public DbSet<UserRelease> UserReleases { get; set; }
public DbSet<ApplicationRole> UserRoles { get; set; } public DbSet<UserRole> UserRoles { get; set; }
public DbSet<ApplicationUser> Users { get; set; } public DbSet<User> Users { get; set; }
public DbSet<UserTrack> UserTracks { get; set; } public DbSet<UserTrack> UserTracks { get; set; }
public DbSet<InviteToken> InviteTokens { get; set; } public DbSet<InviteToken> InviteTokens { get; set; }
@ -79,142 +79,737 @@ namespace Roadie.Library.Data.Context
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); // base.OnModelCreating(builder);
//builder builder.Entity<Artist>(entity =>
// .Entity<ScanHistory>() {
// .Property(e => e.Status) entity
// .HasConversion( .Property(e => e.BandStatus)
// v => v.ToString(), .HasConversion(
// v => string.IsNullOrEmpty(v) ? Statuses.Ok : (Statuses)Enum.Parse(typeof(Statuses), v)) v => v.ToString(),
// .HasDefaultValue(Statuses.Ok); v => string.IsNullOrEmpty(v) ? BandStatus.Unknown : (BandStatus)Enum.Parse(typeof(BandStatus), v))
.HasDefaultValue(BandStatus.Unknown);
//builder entity.HasIndex(e => e.Name)
// .Entity<Artist>() .HasName("ix_artist_name")
// .Property(e => e.Status) .IsUnique();
// .HasConversion(
// v => v.ToString(),
// v => string.IsNullOrEmpty(v) ? Statuses.Incomplete : (Statuses)Enum.Parse(typeof(Statuses), v))
// .HasDefaultValue(Statuses.Incomplete);
builder entity.HasIndex(e => e.RoadieId)
.Entity<Release>() .HasName("ix_artist_roadieId");
.Property(e => e.ReleaseType)
.HasConversion(
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? ReleaseType.Unknown
: (ReleaseType)Enum.Parse(typeof(ReleaseType), v))
.HasDefaultValue(ReleaseType.Release);
builder entity.HasIndex(e => e.SortName)
.Entity<Release>() .HasName("ix_artist_sortname")
.Property(e => e.LibraryStatus) .IsUnique();
.HasConversion( });
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? LibraryStatus.Incomplete
: (LibraryStatus)Enum.Parse(typeof(LibraryStatus), v))
.HasDefaultValue(LibraryStatus.Incomplete);
builder builder.Entity<ArtistAssociation>(entity =>
.Entity<Collection>() {
.Property(e => e.CollectionType) entity.HasIndex(e => e.AssociatedArtistId)
.HasConversion( .HasName("ix_associatedArtistId");
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? CollectionType.Unknown
: (CollectionType)Enum.Parse(typeof(CollectionType), v))
.HasDefaultValue(CollectionType.Unknown);
builder entity.HasIndex(e => new { e.ArtistId, e.AssociatedArtistId })
.Entity<Artist>() .HasName("ix__artistAssociation");
.Property(e => e.BandStatus)
.HasConversion(
v => v.ToString(),
v => string.IsNullOrEmpty(v) ? BandStatus.Unknown : (BandStatus)Enum.Parse(typeof(BandStatus), v))
.HasDefaultValue(BandStatus.Unknown);
//builder entity.HasOne(d => d.Artist)
// .Entity<Bookmark>() .WithMany(p => p.AssociatedArtists)
// .Property(e => e.BookmarkType) .HasForeignKey(d => d.ArtistId)
// .HasConversion( .OnDelete(DeleteBehavior.Cascade)
// v => v.ToString(), .HasConstraintName("artistAssociation_ibfk_1");
// v => string.IsNullOrEmpty(v) ? BookmarkType.Unknown : (BookmarkType)Enum.Parse(typeof(BookmarkType), v)) });
// .HasDefaultValue(BookmarkType.Unknown);
builder.Entity<Release>() builder.Entity<ArtistGenre>(entity =>
.HasOne(d => d.Artist) {
.WithMany(p => p.Releases) entity.HasIndex(e => e.ArtistId)
.HasForeignKey(d => d.ArtistId) .HasName("ix_artistGenreTable_artistId");
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("release_ibfk_1");
builder.Entity<ReleaseLabel>() entity.HasIndex(e => e.GenreId)
.HasOne(rl => rl.Release) .HasName("ix_artistGenre_genreId");
.WithMany(r => r.Labels)
.HasForeignKey(rl => rl.ReleaseId);
builder.Entity<ReleaseLabel>() entity.HasIndex(e => new { e.ArtistId, e.GenreId })
.HasOne(rl => rl.Label) .HasName("ix__artistGenreAssociation");
.WithMany(l => l.ReleaseLabels)
.HasForeignKey(rl => rl.LabelId);
builder.Entity<ReleaseMedia>() entity.HasOne(d => d.Artist)
.HasMany(rm => rm.Tracks) .WithMany(p => p.Genres)
.WithOne(t => t.ReleaseMedia) .HasForeignKey(d => d.ArtistId)
.HasForeignKey(rm => rm.ReleaseMediaId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("artistGenreTable_ibfk_1");
builder.Entity<ReleaseMedia>() entity.HasOne(d => d.Genre)
.HasOne(rm => rm.Release) .WithMany(p => p.Artists)
.WithMany(r => r.Medias) .HasForeignKey(d => d.GenreId)
.HasForeignKey(r => r.ReleaseId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("artistGenreTable_ibfk_2");
});
builder.Entity<ReleaseGenre>() builder.Entity<ArtistSimilar>(entity =>
.HasKey(rg => new { rg.ReleaseId, rg.GenreId }); {
entity.HasIndex(e => e.SimilarArtistId)
.HasName("ix_similarArtistId");
builder.Entity<ReleaseGenre>() entity.HasIndex(e => new { e.ArtistId, e.SimilarArtistId })
.HasOne(rg => rg.Release) .HasName("ix_artistSimilar");
.WithMany(r => r.Genres)
.HasForeignKey(rg => rg.ReleaseId);
builder.Entity<ReleaseGenre>() entity.HasOne(d => d.Artist)
.HasOne(rg => rg.Genre) .WithMany(p => p.SimilarArtists)
.WithMany(g => g.Releases) .HasForeignKey(d => d.ArtistId)
.HasForeignKey(rg => rg.GenreId); .HasConstraintName("artistSimilar_ibfk_1");
});
builder.Entity<ArtistGenre>() builder.Entity<Bookmark>(entity =>
.HasKey(rg => new { rg.ArtistId, rg.GenreId }); {
entity.HasIndex(e => e.RoadieId)
.HasName("ix_bookmark_roadieId");
builder.Entity<ArtistGenre>() entity.HasIndex(e => e.UserId)
.HasOne(rg => rg.Artist) .HasName("ix_bookmark_userId");
.WithMany(r => r.Genres)
.HasForeignKey(rg => rg.ArtistId);
builder.Entity<ArtistGenre>() entity.HasIndex(e => new { e.BookmarkType, e.BookmarkTargetId, e.UserId })
.HasOne(rg => rg.Genre) .HasName("ix_bookmark_bookmarkType")
.WithMany(g => g.Artists) .IsUnique();
.HasForeignKey(rg => rg.GenreId);
builder.Entity<CollectionRelease>() entity.HasOne(d => d.User)
.HasOne(cr => cr.Release) .WithMany(p => p.Bookmarks)
.WithMany(r => r.Collections) .HasForeignKey(d => d.UserId)
.HasForeignKey(cr => cr.ReleaseId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("bookmark_ibfk_1");
});
builder.Entity<CollectionRelease>() builder.Entity<ChatMessage>(entity =>
.HasOne(cr => cr.Collection) {
.WithMany(c => c.Releases) entity.HasIndex(e => e.UserId)
.HasForeignKey(cr => cr.CollectionId); .HasName("ix__chatMessage_user");
builder.Entity<Bookmark>() entity.HasOne(d => d.User)
.HasOne(b => b.User) .WithMany(p => p.ChatMessages)
.WithMany(u => u.Bookmarks) .HasForeignKey(d => d.UserId)
.HasForeignKey(b => b.UserId); .HasConstraintName("chatMessage_ibfk_1");
});
//builder.Entity<Track>() builder.Entity<Collection>(entity =>
// .HasOne(t => t.TrackArtist) {
// .WithMany(a => a.Tracks) entity
// .HasForeignKey(t => t.ArtistId); .Property(e => e.CollectionType)
.HasConversion(
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? CollectionType.Unknown
: (CollectionType)Enum.Parse(typeof(CollectionType), v))
.HasDefaultValue(CollectionType.Unknown);
entity.HasIndex(e => e.MaintainerId)
.HasName("ix_collection_maintainerId");
entity.HasIndex(e => e.Name)
.HasName("ix_collection_name")
.IsUnique();
entity.HasIndex(e => e.RoadieId)
.HasName("ix_collection_roadieId");
entity.HasOne(d => d.Maintainer)
.WithMany(p => p.Collections)
.HasForeignKey(d => d.MaintainerId)
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("collection_ibfk_1");
});
builder.Entity<CollectionMissing>(entity =>
{
entity.HasIndex(e => e.CollectionId)
.HasName("ix_collection_collectionId");
entity.HasOne(d => d.Collection)
.WithMany(p => p.MissingReleases)
.HasForeignKey(d => d.CollectionId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("collection_missing_ibfk_1");
});
builder.Entity<CollectionRelease>(entity =>
{
entity.HasIndex(e => e.ReleaseId)
.HasName("ix_collectionrelease_releaseId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_collectionrelease_roadieId");
entity.HasIndex(e => new { e.CollectionId, e.ReleaseId })
.HasName("ix__collection_release");
entity.HasOne(d => d.Collection)
.WithMany(p => p.Releases)
.HasForeignKey(d => d.CollectionId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("collectionrelease_ibfk_2");
entity.HasOne(d => d.Release)
.WithMany(p => p.Collections)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("collectionrelease_ibfk_1");
});
builder.Entity<Comment>(entity =>
{
entity.HasIndex(e => e.ArtistId)
.HasName("ix_commentartist_ibfk_1");
entity.HasIndex(e => e.CollectionId)
.HasName("ix_commentcollection_ibfk_1");
entity.HasIndex(e => e.GenreId)
.HasName("ix_commentgenre_ibfk_1");
entity.HasIndex(e => e.LabelId)
.HasName("ix_commentlabel_ibfk_1");
entity.HasIndex(e => e.PlaylistId)
.HasName("ix_commentplaylist_ibfk_1");
entity.HasIndex(e => e.ReleaseId)
.HasName("ix_commentrelease_ibfk_1");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_comment_roadieId");
entity.HasIndex(e => e.TrackId)
.HasName("ix_commenttrack_ibfk_1");
entity.HasIndex(e => e.UserId)
.HasName("ix_commentuser_ibfk_1");
entity.HasOne(d => d.Artist)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentartist_ibfk_1");
entity.HasOne(d => d.Collection)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.CollectionId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentcollection_ibfk_1");
entity.HasOne(d => d.Genre)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.GenreId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentgenre_ibfk_1");
entity.HasOne(d => d.Label)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.LabelId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentlabel_ibfk_1");
entity.HasOne(d => d.Playlist)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.PlaylistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentplaylist_ibfk_1");
entity.HasOne(d => d.Release)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commentrelease_ibfk_1");
entity.HasOne(d => d.Track)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.TrackId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("commenttrack_ibfk_1");
entity.HasOne(d => d.User)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.UserId)
.HasConstraintName("commentuser_ibfk_1");
});
builder.Entity<CommentReaction>(entity =>
{
entity.HasIndex(e => e.CommentId)
.HasName("ix_commentReactioncomment_ibfk_1");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_commentReaction_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_commentReactionuser_ibfk_1");
entity.HasIndex(e => new { e.UserId, e.CommentId })
.HasName("ix_commentReaction_userId")
.IsUnique();
entity.HasOne(d => d.Comment)
.WithMany(p => p.Reactions)
.HasForeignKey(d => d.CommentId)
.HasConstraintName("commentReactioncomment_ibfk_1");
entity.HasOne(d => d.User)
.WithMany(p => p.CommentReactions)
.HasForeignKey(d => d.UserId)
.HasConstraintName("commentReactionuser_ibfk_1");
});
builder.Entity<Credit>(entity =>
{
entity.HasIndex(e => e.ArtistId)
.HasName("ix_credit_artist_ibfk_1");
entity.HasIndex(e => e.CreditCategoryId)
.HasName("ix_credit_category_ibfk_1");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_credit_roadieId");
entity.HasIndex(e => new { e.ReleaseId, e.Id })
.HasName("ix_creditCreditandRelease");
entity.HasIndex(e => new { e.TrackId, e.Id })
.HasName("ix_creditCreditandTrack");
entity.HasOne(d => d.Artist)
.WithMany(p => p.Credits)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("credit_artist_ibfk_1");
entity.HasOne(d => d.CreditCategory)
.WithMany(p => p.Credits)
.HasForeignKey(d => d.CreditCategoryId)
.HasConstraintName("credit_category_ibfk_1");
entity.HasOne(d => d.Release)
.WithMany(p => p.Credits)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("credit_release_ibfk_1");
entity.HasOne(d => d.Track)
.WithMany(p => p.Credits)
.HasForeignKey(d => d.TrackId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("credit_track_ibfk_1");
});
builder.Entity<CreditCategory>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_creditCategory_roadieId");
});
builder.Entity<Genre>(entity =>
{
entity.HasIndex(e => e.Name)
.HasName("ix_genre_name")
.IsUnique();
entity.HasIndex(e => e.NormalizedName)
.HasName("ix_genre_normalizedName");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_genre_roadieId");
});
builder.Entity<InviteToken>(entity =>
{
entity.HasIndex(e => e.CreatedByUserId)
.HasName("inviteToken_fk_1");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_inviteToken_roadieId");
entity.HasOne(d => d.CreatedByUser)
.WithMany(p => p.InviteTokens)
.HasForeignKey(d => d.CreatedByUserId)
.HasConstraintName("inviteToken_fk_1");
});
builder.Entity<Label>(entity =>
{
entity.HasIndex(e => e.Name)
.HasName("ix_label_name")
.IsUnique();
entity.HasIndex(e => e.RoadieId)
.HasName("ix_label_roadieId");
});
builder.Entity<Playlist>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_playlist_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_playlist_userId");
entity.HasIndex(e => new { e.Name, e.UserId })
.HasName("ix_playlist_name")
.IsUnique();
entity.HasOne(d => d.User)
.WithMany(p => p.Playlists)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("playlist_ibfk_1");
});
builder.Entity<PlaylistTrack>(entity =>
{
entity.HasIndex(e => e.PlayListId)
.HasName("ix_playListId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_playlisttrack_roadieId");
entity.HasIndex(e => e.TrackId)
.HasName("trackId");
entity.HasOne(d => d.Playlist)
.WithMany(p => p.Tracks)
.HasForeignKey(d => d.PlayListId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("playlisttrack_ibfk_2");
entity.HasOne(d => d.Track)
.WithMany(p => p.Playlists)
.HasForeignKey(d => d.TrackId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("playlisttrack_ibfk_1");
});
builder.Entity<Release>(entity =>
{
entity
.Property(e => e.ReleaseType)
.HasConversion(
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? ReleaseType.Unknown
: (ReleaseType)Enum.Parse(typeof(ReleaseType), v))
.HasDefaultValue(ReleaseType.Release);
entity
.Property(e => e.LibraryStatus)
.HasConversion(
v => v.ToString(),
v => string.IsNullOrEmpty(v)
? LibraryStatus.Incomplete
: (LibraryStatus)Enum.Parse(typeof(LibraryStatus), v))
.HasDefaultValue(LibraryStatus.Incomplete);
entity.HasIndex(e => e.RoadieId)
.HasName("ix_release_roadieId");
entity.HasIndex(e => e.Title)
.HasName("ix_release_title");
entity.HasIndex(e => new { e.ArtistId, e.Title })
.HasName("ix_releaseArtistAndTitle")
.IsUnique();
entity.HasOne(d => d.Artist)
.WithMany(p => p.Releases)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("release_ibfk_1");
});
builder.Entity<ReleaseGenre>(entity =>
{
entity.HasIndex(e => e.GenreId)
.HasName("ix_releaseGenre_genreId");
entity.HasIndex(e => new { e.ReleaseId, e.GenreId })
.HasName("ix_releaseGenreTableReleaseAndGenre");
entity.HasOne(d => d.Genre)
.WithMany(p => p.Releases)
.HasForeignKey(d => d.GenreId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaseGenreTable_ibfk_2");
entity.HasOne(d => d.Release)
.WithMany(p => p.Genres)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaseGenreTable_ibfk_1");
});
builder.Entity<ReleaseLabel>(entity =>
{
entity.HasIndex(e => e.LabelId)
.HasName("ix_releaselabel_labelId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_releaselabel_roadieId");
entity.HasIndex(e => new { e.ReleaseId, e.LabelId })
.HasName("ix_release_label");
entity.HasOne(d => d.Label)
.WithMany(p => p.ReleaseLabels)
.HasForeignKey(d => d.LabelId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaselabel_ibfk_2");
entity.HasOne(d => d.Release)
.WithMany(p => p.Labels)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaselabel_ibfk_1");
});
builder.Entity<ReleaseMedia>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_releasemedia_roadieId");
entity.HasIndex(e => new { e.ReleaseId, e.MediaNumber })
.HasName("ix_releasemedia_releaseId");
entity.HasOne(d => d.Release)
.WithMany(p => p.Medias)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releasemedia_ibfk_1");
});
builder.Entity<Request>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_request_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_requestartist_ibfk_1");
entity.HasOne(d => d.User)
.WithMany(p => p.Requests)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("requestartist_ibfk_1");
});
builder.Entity<ScanHistory>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_scanHistory_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_rscanHistoryt_ibfk_1");
});
builder.Entity<Submission>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_submission_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_submission_ibfk_1");
entity.HasOne(d => d.User)
.WithMany(p => p.Submissions)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("submission_ibfk_1");
});
builder.Entity<Track>(entity =>
{
entity.HasIndex(e => e.ArtistId)
.HasName("ix_track_artistId");
entity.HasIndex(e => e.Hash)
.HasName("ix_track_hash")
.IsUnique();
entity.HasIndex(e => e.ReleaseMediaId)
.HasName("ix_track_releaseMediaId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_track_roadieId");
entity.HasIndex(e => e.Title)
.HasName("ix_track_title");
entity.HasIndex(e => new { e.ReleaseMediaId, e.TrackNumber })
.HasName("ix_track_unique_to_eleasemedia")
.IsUnique();
entity.HasOne(d => d.TrackArtist)
.WithMany(p => p.Tracks)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("track_artist_ibfk_1");
entity.HasOne(d => d.ReleaseMedia)
.WithMany(p => p.Tracks)
.HasForeignKey(d => d.ReleaseMediaId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("track_ibfk_1");
});
builder.Entity<UserQue>(entity =>
{
entity.HasIndex(e => e.TrackId)
.HasName("ix_userQue_ibfk_2");
entity.HasIndex(e => e.UserId)
.HasName("ix_user");
entity.HasOne(d => d.Track)
.WithMany(p => p.UserQues)
.HasForeignKey(d => d.TrackId)
.HasConstraintName("userQue_ibfk_2");
entity.HasOne(d => d.User)
.WithMany(p => p.UserQues)
.HasForeignKey(d => d.UserId)
.HasConstraintName("userQue_ibfk_1");
});
builder.Entity<UserArtist>(entity =>
{
entity.HasIndex(e => e.ArtistId)
.HasName("ix_userartist_artistId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_userartist_roadieId");
entity.HasIndex(e => new { e.UserId, e.ArtistId })
.HasName("ix_userartist_userId")
.IsUnique();
entity.HasOne(d => d.Artist)
.WithMany(p => p.UserArtists)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("userartist_ibfk_2");
entity.HasOne(d => d.User)
.WithMany(p => p.ArtistRatings)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("userartist_ibfk_1");
});
builder.Entity<UserRelease>(entity =>
{
entity.HasIndex(e => e.ReleaseId)
.HasName("ix_userrelease_releaseId");
entity.HasIndex(e => e.RoadieId)
.HasName("ix_userrelease_roadieId");
entity.HasIndex(e => new { e.UserId, e.ReleaseId })
.HasName("ix_userrelease_userId_ix")
.IsUnique();
entity.HasOne(d => d.Release)
.WithMany(p => p.UserRelases)
.HasForeignKey(d => d.ReleaseId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("userrelease_ibfk_2");
entity.HasOne(d => d.User)
.WithMany(p => p.UserReleases)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("userrelease_ibfk_1");
});
builder.Entity<UserTrack>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_usertrack_roadieId");
entity.HasIndex(e => e.TrackId)
.HasName("ix_usertrack_trackId");
entity.HasIndex(e => new { e.UserId, e.TrackId })
.HasName("ix_usertrack_userId_ix")
.IsUnique();
entity.HasOne(d => d.Track)
.WithMany(p => p.UserTracks)
.HasForeignKey(d => d.TrackId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("usertrack_ibfk_2");
entity.HasOne(d => d.User)
.WithMany(p => p.UserTracks)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("usertrack_ibfk_1");
});
builder.Entity<User>(entity =>
{
entity.HasIndex(e => e.Email)
.HasName("ix_user_email")
.IsUnique();
entity.HasIndex(e => e.RoadieId)
.HasName("ix_user_roadieId");
entity.HasIndex(e => e.UserName)
.HasName("ix_user_username")
.IsUnique();
});
builder.Entity<UserClaims>(entity =>
{
entity.HasIndex(e => e.UserId)
.HasName("ix_userClaims_userId");
entity.HasOne(d => d.User)
.WithMany(p => p.UserClaims)
.HasForeignKey(d => d.UserId)
.HasConstraintName("userClaims_ibfk_1");
});
builder.Entity<UserRoleClaims>(entity =>
{
entity.HasIndex(e => e.RoleId)
.HasName("ix_userRoleClaims_userRoleId");
entity.HasOne(d => d.UserRole)
.WithMany(p => p.RoleClaims)
.HasForeignKey(d => d.RoleId)
.HasConstraintName("userRoleClaims_ibfk_1");
});
builder.Entity<UserRole>(entity =>
{
entity.HasIndex(e => e.Name)
.HasName("ix_userrole_name")
.IsUnique();
entity.HasIndex(e => e.RoadieId)
.HasName("ix_userrole_roadieId");
});
builder.Entity<UsersInRoles>(entity =>
{
entity.HasIndex(e => e.UserId)
.HasName("ix_usersInRoles_userId");
entity.HasIndex(e => e.RoleId)
.HasName("ix_usersInRoles_userRoleId");
entity.HasOne(d => d.User)
.WithMany(p => p.UserRoles)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("usersInRoles_ibfk_1");
entity.HasOne(d => d.Role)
.WithMany(p => p.UserRoles)
.HasForeignKey(d => d.RoleId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("usersInRoles_ibfk_2");
});
} }
Task<EntityEntry> IRoadieDbContext.AddAsync(object entity, CancellationToken cancellationToken) => throw new NotImplementedException(); Task<EntityEntry> IRoadieDbContext.AddAsync(object entity, CancellationToken cancellationToken) => throw new NotImplementedException();

View file

@ -1,6 +1,4 @@
using Roadie.Library.Enums; using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Data namespace Roadie.Library.Data
@ -39,5 +37,21 @@ namespace Roadie.Library.Data
[NotMapped] [NotMapped]
public override string AlternateNames { get; set; } public override string AlternateNames { get; set; }
[ForeignKey(nameof(ArtistId))]
[InverseProperty("Credits")]
public virtual Artist Artist { get; set; }
[ForeignKey(nameof(CreditCategoryId))]
[InverseProperty("Credits")]
public virtual CreditCategory CreditCategory { get; set; }
[ForeignKey(nameof(ReleaseId))]
[InverseProperty("Credits")]
public virtual Release Release { get; set; }
[ForeignKey(nameof(TrackId))]
[InverseProperty("Credits")]
public virtual Track Track { get; set; }
} }
} }

View file

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Data namespace Roadie.Library.Data
@ -16,5 +17,13 @@ namespace Roadie.Library.Data
[NotMapped] [NotMapped]
public override string SortName { get; set; } public override string SortName { get; set; }
[InverseProperty("CreditCategory")]
public virtual ICollection<Credit> Credits { get; set; }
public CreditCategory()
{
Credits = new HashSet<Credit>();
}
} }
} }

View file

@ -7,7 +7,9 @@ namespace Roadie.Library.Data
{ {
public abstract class EntityBase public abstract class EntityBase
{ {
[Column("createdDate")] [Required] public DateTime CreatedDate { get; set; } [Column("createdDate")]
[Required]
public DateTime CreatedDate { get; set; }
[Column("id")] [Column("id")]
[Key] [Key]
@ -16,14 +18,14 @@ namespace Roadie.Library.Data
public virtual bool? IsLocked { get; set; } public virtual bool? IsLocked { get; set; }
[Column("lastUpdated")] [Column("lastUpdated")]
public DateTime? LastUpdated { get; set; } public DateTime? LastUpdated { get; set; }
[Column("RoadieId")] [Column("RoadieId")]
[Required] [Required]
public Guid RoadieId { get; set; } public Guid RoadieId { get; set; }
[Column("status")] [Column("status")]
public Statuses? Status { get; set; } public Statuses? Status { get; set; }
public EntityBase() public EntityBase()

View file

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -8,12 +7,14 @@ namespace Roadie.Library.Data
[Table("genre")] [Table("genre")]
public partial class Genre : EntityBase public partial class Genre : EntityBase
{ {
public ICollection<ArtistGenre> Artists { get; set; } [InverseProperty("Genre")]
public virtual ICollection<ArtistGenre> Artists { get; set; }
public ICollection<Comment> Comments { get; set; } [InverseProperty("Genre")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("name")] [Column("name")]
[MaxLength(100)] [MaxLength(100)]
[Required] [Required]
public string Name { get; set; } public string Name { get; set; }
@ -35,14 +36,12 @@ namespace Roadie.Library.Data
[MaxLength(65535)] [MaxLength(65535)]
public string Tags { get; set; } public string Tags { get; set; }
[Obsolete("Images moved to file system")] [Column("normalizedName")]
[Column("thumbnail", TypeName = "blob")] [MaxLength(100)]
[MaxLength(65535)] public string NormalizedName { get; set; }
public byte[] Thumbnail { get; set; }
[Column("normalizedName")] [MaxLength(100)] public string NormalizedName { get; set; } [InverseProperty("Genre")]
public virtual ICollection<ReleaseGenre> Releases { get; set; }
public ICollection<ReleaseGenre> Releases { get; set; }
public Genre() public Genre()
{ {

View file

@ -26,23 +26,13 @@ namespace Roadie.Library.Data
public string PathToImage(IRoadieSettings configuration, bool makeFolderIfNotExist = false) public string PathToImage(IRoadieSettings configuration, bool makeFolderIfNotExist = false)
{ {
var folder = FolderPathHelper.GenrePath(configuration, SortNameValue); var folder = FolderPathHelper.GenrePath(configuration, SortNameValue);
if(!Directory.Exists(folder) && makeFolderIfNotExist) if (!Directory.Exists(folder) && makeFolderIfNotExist)
{ {
Directory.CreateDirectory(folder); Directory.CreateDirectory(folder);
} }
return Path.Combine(folder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg"); return Path.Combine(folder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg");
} }
/// <summary>
/// Returns a full file path to the Label Image
/// </summary>
[Obsolete("This is only here for migration will be removed in future release.")]
public string OldPathToImage(IRoadieSettings configuration)
{
return Path.Combine(configuration.GenreImageFolder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg");
}
public static string CacheRegionUrn(Guid Id) public static string CacheRegionUrn(Guid Id)
{ {
return string.Format("urn:genre:{0}", Id); return string.Format("urn:genre:{0}", Id);

View file

@ -1,6 +1,5 @@
using Roadie.Library.Identity; using Roadie.Library.Identity;
using System; using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Data namespace Roadie.Library.Data
@ -8,6 +7,10 @@ namespace Roadie.Library.Data
[Table("inviteToken")] [Table("inviteToken")]
public class InviteToken : EntityBase public class InviteToken : EntityBase
{ {
[ForeignKey(nameof(CreatedByUserId))]
[InverseProperty(nameof(User.InviteTokens))]
public virtual User CreatedByUser { get; set; }
[Column("createdByUserId")] [Column("createdByUserId")]
public int CreatedByUserId { get; set; } public int CreatedByUserId { get; set; }
@ -17,4 +20,4 @@ namespace Roadie.Library.Data
[NotMapped] [NotMapped]
public new bool IsLocked { get; set; } public new bool IsLocked { get; set; }
} }
} }

View file

@ -8,13 +8,19 @@ namespace Roadie.Library.Data
[Table("label")] [Table("label")]
public partial class Label : BeginAndEndNamedEntityBase public partial class Label : BeginAndEndNamedEntityBase
{ {
[Column("artistCount")] public int? ArtistCount { get; set; } [Column("artistCount")]
public int? ArtistCount { get; set; }
public ICollection<Comment> Comments { get; set; } [InverseProperty("Label")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("discogsId")] [MaxLength(50)] public string DiscogsId { get; set; } [Column("discogsId")]
[MaxLength(50)]
public string DiscogsId { get; set; }
[Column("imageUrl")] [MaxLength(500)] public string ImageUrl { get; set; } [Column("imageUrl")]
[MaxLength(500)]
public string ImageUrl { get; set; }
[Column("musicBrainzId")] [Column("musicBrainzId")]
[MaxLength(100)] [MaxLength(100)]
@ -24,11 +30,14 @@ namespace Roadie.Library.Data
[MaxLength(65535)] [MaxLength(65535)]
public string Profile { get; set; } public string Profile { get; set; }
[Column("releaseCount")] public int? ReleaseCount { get; set; } [Column("releaseCount")]
public int? ReleaseCount { get; set; }
public ICollection<ReleaseLabel> ReleaseLabels { get; set; } [InverseProperty("Label")]
public virtual ICollection<ReleaseLabel> ReleaseLabels { get; set; }
[Column("trackCount")] public int? TrackCount { get; set; } [Column("trackCount")]
public int? TrackCount { get; set; }
public Label() public Label()
{ {

View file

@ -2,10 +2,7 @@
using Roadie.Library.Extensions; using Roadie.Library.Extensions;
using Roadie.Library.Utility; using Roadie.Library.Utility;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Roadie.Library.Data namespace Roadie.Library.Data
{ {
@ -23,8 +20,6 @@ namespace Roadie.Library.Data
} }
} }
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
/// <summary> /// <summary>
/// Returns a full file path to the Label Image /// Returns a full file path to the Label Image
/// </summary> /// </summary>
@ -38,15 +33,6 @@ namespace Roadie.Library.Data
return Path.Combine(folder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg"); return Path.Combine(folder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg");
} }
/// <summary>
/// Returns a full file path to the Label Image
/// </summary>
[Obsolete("This is only here for migration will be removed in future release.")]
public string OldPathToImage(IRoadieSettings configuration)
{
return Path.Combine(configuration.LabelImageFolder, $"{ SortNameValue.ToFileNameFriendly() } [{ Id }].jpg");
}
public bool IsValid => !string.IsNullOrEmpty(Name); public bool IsValid => !string.IsNullOrEmpty(Name);
public static string CacheRegionUrn(Guid Id) public static string CacheRegionUrn(Guid Id)

View file

@ -1,5 +1,4 @@
using System; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Data namespace Roadie.Library.Data
@ -14,8 +13,8 @@ namespace Roadie.Library.Data
[Column("name")] [Column("name")]
public virtual string Name { get; set; } public virtual string Name { get; set; }
[Column("sortName")] [Column("sortName")]
[MaxLength(250)] [MaxLength(250)]
public virtual string SortName { get; set; } public virtual string SortName { get; set; }
public virtual string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName; public virtual string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;

View file

@ -8,31 +8,42 @@ namespace Roadie.Library.Data
[Table("playlist")] [Table("playlist")]
public partial class Playlist : NamedEntityBase public partial class Playlist : NamedEntityBase
{ {
public ICollection<Comment> Comments { get; set; } [InverseProperty("Playlist")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("Description")] [Column("Description")]
[MaxLength(1000)] [MaxLength(1000)]
public string Description { get; set; } public string Description { get; set; }
[Column("duration")] public int? Duration { get; set; } [Column("duration")]
public int? Duration { get; set; }
[Column("isPublic")] public bool IsPublic { get; set; } [Column("isPublic")]
public bool IsPublic { get; set; }
[Column("releaseCount")] public short ReleaseCount { get; set; } [Column("releaseCount")]
public short ReleaseCount { get; set; }
[NotMapped] public new string SortName { get; set; } [NotMapped]
public new string SortName { get; set; }
[Column("trackCount")] public short TrackCount { get; set; } [Column("trackCount")]
public short TrackCount { get; set; }
public ICollection<PlaylistTrack> Tracks { get; set; } [InverseProperty("Playlist")]
public virtual ICollection<PlaylistTrack> Tracks { get; set; }
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("Playlists")]
public virtual User User { get; set; }
[Column("userId")] public int? UserId { get; set; } [Column("userId")]
public int? UserId { get; set; }
public Playlist() public Playlist()
{ {
Comments = new HashSet<Comment>(); Comments = new HashSet<Comment>();
Tracks = new HashSet<PlaylistTrack>();
} }
} }
} }

View file

@ -1,7 +1,6 @@
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
using Roadie.Library.Extensions; using Roadie.Library.Extensions;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
namespace Roadie.Library.Data namespace Roadie.Library.Data

View file

@ -6,14 +6,21 @@ namespace Roadie.Library.Data
[Table("playlisttrack")] [Table("playlisttrack")]
public class PlaylistTrack : EntityBase public class PlaylistTrack : EntityBase
{ {
[Column("listNumber")] [Required] public int ListNumber { get; set; } [Column("listNumber")]
[Required]
public int ListNumber { get; set; }
public Playlist Playlist { get; set; } [ForeignKey(nameof(PlayListId))]
[InverseProperty("Tracks")]
public virtual Playlist Playlist { get; set; }
[Column("playListId")] public int PlayListId { get; set; } [Column("playListId")]
public int PlayListId { get; set; }
public Track Track { get; set; } public virtual Track Track { get; set; }
[Column("trackId")] public int TrackId { get; set; } [Column("trackId")]
[InverseProperty("PlaylistTracks")]
public int TrackId { get; set; }
} }
} }

View file

@ -0,0 +1,33 @@
using Roadie.Library.Enums;
using System;
using System.Text.Json.Serialization;
namespace Roadie.Library.Data
{
[Serializable]
public class PositionArtistRelease
{
public string Artist { get; set; }
/// <summary>
/// This is the index (position in the list regardless of the position number)
/// </summary>
[JsonIgnore]
public int Index { get; set; }
/// <summary>
/// This is the position number for the list (can be a year "1984" can be a number "14")
/// </summary>
public int Position { get; set; }
public string Release { get; set; }
[JsonIgnore]
public Statuses Status { get; set; }
[JsonPropertyName("Status")]
public string StatusVerbose => Status.ToString();
public override string ToString() => string.Format("Position [{0}], Artist [{1}], Release [{2}]", Position, Artist, Release);
}
}

View file

@ -14,78 +14,101 @@ namespace Roadie.Library.Data
[MaxLength(65535)] [MaxLength(65535)]
public string AlternateNames { get; set; } public string AlternateNames { get; set; }
[Column("amgId")] [MaxLength(50)] public string AmgId { get; set; } [Column("amgId")]
[MaxLength(50)]
public string AmgId { get; set; }
public Artist Artist { get; set; } public virtual Artist Artist { get; set; }
[Column("artistId")] [Required] public int ArtistId { get; set; } [Column("artistId")]
[Required]
public int ArtistId { get; set; }
public ICollection<CollectionRelease> Collections { get; set; } [InverseProperty("Release")]
public virtual ICollection<CollectionRelease> Collections { get; set; }
public ICollection<Comment> Comments { get; set; } [InverseProperty("Release")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("discogsId")] [MaxLength(50)] public string DiscogsId { get; set; } [Column("discogsId")]
[MaxLength(50)]
public string DiscogsId { get; set; }
[Column("duration")] public int? Duration { get; set; } [Column("duration")]
public int? Duration { get; set; }
public ICollection<ReleaseGenre> Genres { get; set; } [InverseProperty("Release")]
public virtual ICollection<ReleaseGenre> Genres { get; set; }
[NotMapped] [NotMapped]
public IEnumerable<Imaging.IImage> Images { get; set; } = Enumerable.Empty<Imaging.IImage>(); public IEnumerable<Imaging.IImage> Images { get; set; } = Enumerable.Empty<Imaging.IImage>();
public Imaging.IImage ThumbnailImage => Images.OrderBy(x => x.SortOrder).FirstOrDefault(); public Imaging.IImage ThumbnailImage => Images.OrderBy(x => x.SortOrder).FirstOrDefault();
[Column("isVirtual")] public bool? IsVirtual { get; set; } [Column("isVirtual")]
public bool? IsVirtual { get; set; }
[Column("itunesId")] [MaxLength(100)] public string ITunesId { get; set; } [Column("itunesId")]
[MaxLength(100)]
public string ITunesId { get; set; }
public ICollection<ReleaseLabel> Labels { get; set; } [InverseProperty("Release")]
public virtual ICollection<ReleaseLabel> Labels { get; set; }
[Column("lastFMId")] [MaxLength(50)] public string LastFMId { get; set; } [Column("lastFMId")]
[MaxLength(50)]
public string LastFMId { get; set; }
[Column("lastFMSummary", TypeName = "text")] [Column("lastFMSummary", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string LastFMSummary { get; set; } public string LastFMSummary { get; set; }
[Column("lastPlayed")] public DateTime? LastPlayed { get; set; } [Column("lastPlayed")]
public DateTime? LastPlayed { get; set; }
[Column("libraryStatus")] public LibraryStatus? LibraryStatus { get; set; } [Column("libraryStatus")]
public LibraryStatus? LibraryStatus { get; set; }
[Column("mediaCount")] public short? MediaCount { get; set; } [Column("mediaCount")]
public short? MediaCount { get; set; }
public ICollection<ReleaseMedia> Medias { get; set; } [InverseProperty("Release")]
public virtual ICollection<ReleaseMedia> Medias { get; set; }
[Column("musicBrainzId")] [Column("musicBrainzId")]
[MaxLength(100)] [MaxLength(100)]
public string MusicBrainzId { get; set; } public string MusicBrainzId { get; set; }
[Column("playedCount")] public int? PlayedCount { get; set; } [Column("playedCount")]
public int? PlayedCount { get; set; }
[Column("profile", TypeName = "text")] [Column("profile", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string Profile { get; set; } public string Profile { get; set; }
[Column("rank")] public decimal? Rank { get; set; } [Column("rank")]
public decimal? Rank { get; set; }
[Column("rating")] public short? Rating { get; set; } [Column("rating")]
public short? Rating { get; set; }
[Column("releaseDate")] public DateTime? ReleaseDate { get; set; } [Column("releaseDate")]
public DateTime? ReleaseDate { get; set; }
[Column("releaseType")] public ReleaseType? ReleaseType { get; set; } [Column("releaseType")]
public ReleaseType? ReleaseType { get; set; }
[Column("spotifyId")] [MaxLength(100)] public string SpotifyId { get; set; } [Column("spotifyId")]
[MaxLength(100)]
public string SpotifyId { get; set; }
[Column("submissionId")] public int? SubmissionId { get; set; } [Column("submissionId")]
public int? SubmissionId { get; set; }
[Column("tags", TypeName = "text")] [Column("tags", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string Tags { get; set; } public string Tags { get; set; }
[Obsolete("Images moved to file system")]
[Column("thumbnail", TypeName = "blob")]
[MaxLength(65535)]
public byte[] Thumbnail { get; set; }
[MaxLength(250)] [MaxLength(250)]
[Column("title")] [Column("title")]
[Required] [Required]
@ -97,21 +120,31 @@ namespace Roadie.Library.Data
public string SortTitleValue => string.IsNullOrEmpty(SortTitle) ? Title : SortTitle; public string SortTitleValue => string.IsNullOrEmpty(SortTitle) ? Title : SortTitle;
[Column("trackCount")] public short TrackCount { get; set; } [Column("trackCount")]
public short TrackCount { get; set; }
[Column("urls", TypeName = "text")] [Column("urls", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string URLs { get; set; } public string URLs { get; set; }
[InverseProperty("Release")]
public virtual ICollection<Credit> Credits { get; set; }
[InverseProperty("Release")]
public virtual ICollection<UserRelease> UserRelases { get; set; }
public Release() public Release()
{ {
Rating = 0; Rating = 0;
ReleaseType = Enums.ReleaseType.Release; ReleaseType = Enums.ReleaseType.Release;
Medias = new HashSet<ReleaseMedia>(); Medias = new HashSet<ReleaseMedia>();
Labels = new HashSet<ReleaseLabel>(); Labels = new HashSet<ReleaseLabel>();
Collections = new HashSet<CollectionRelease>(); Collections = new HashSet<CollectionRelease>();
Genres = new HashSet<ReleaseGenre>(); Genres = new HashSet<ReleaseGenre>();
Comments = new HashSet<Comment>(); Comments = new HashSet<Comment>();
Credits = new HashSet<Credit>();
UserRelases = new HashSet<UserRelease>();
} }
} }
} }

View file

@ -6,17 +6,25 @@ namespace Roadie.Library.Data
[Table("releaseGenreTable")] [Table("releaseGenreTable")]
public class ReleaseGenre public class ReleaseGenre
{ {
public Genre Genre { get; set; } [ForeignKey(nameof(ReleaseId))]
[InverseProperty("Genres")]
public virtual Release Release { get; set; }
[Column("genreId")] [Required] public int? GenreId { get; set; } [Column("releaseId")]
[Required]
public int ReleaseId { get; set; }
[ForeignKey(nameof(GenreId))]
[InverseProperty("Releases")]
public virtual Genre Genre { get; set; }
[Column("genreId")]
[Required]
public int GenreId { get; set; }
[Column("id")]
[Key] [Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; } public int Id { get; set; }
public Release Release { get; set; }
[Column("releaseId")] [Required] public int ReleaseId { get; set; }
} }
} }

View file

@ -5,14 +5,21 @@ namespace Roadie.Library.Data
[Table("releaselabel")] [Table("releaselabel")]
public class ReleaseLabel : BeginAndEndEntityBase public class ReleaseLabel : BeginAndEndEntityBase
{ {
[Column("catalogNumber")] public string CatalogNumber { get; set; } [Column("catalogNumber")]
public string CatalogNumber { get; set; }
public Label Label { get; set; } [ForeignKey(nameof(LabelId))]
[InverseProperty("ReleaseLabels")]
public virtual Label Label { get; set; }
[Column("labelId")] public int LabelId { get; set; } [Column("labelId")]
public int LabelId { get; set; }
public Release Release { get; set; } [ForeignKey(nameof(ReleaseId))]
[InverseProperty("Labels")]
public virtual Release Release { get; set; }
[Column("releaseId")] public int ReleaseId { get; set; } [Column("releaseId")]
public int ReleaseId { get; set; }
} }
} }

View file

@ -7,19 +7,26 @@ namespace Roadie.Library.Data
[Table("releasemedia")] [Table("releasemedia")]
public class ReleaseMedia : EntityBase public class ReleaseMedia : EntityBase
{ {
[Column("releaseMediaNumber")] public short MediaNumber { get; set; } [Column("releaseMediaNumber")]
public short MediaNumber { get; set; }
public Release Release { get; set; } [ForeignKey(nameof(ReleaseId))]
[InverseProperty("Medias")]
public virtual Release Release { get; set; }
[Column("releaseId")] public int ReleaseId { get; set; } [Column("releaseId")]
public int ReleaseId { get; set; }
[Column("releaseSubTitle")] [Column("releaseSubTitle")]
[MaxLength(500)] [MaxLength(500)]
public string SubTitle { get; set; } public string SubTitle { get; set; }
[Column("trackCount")] [Required] public short TrackCount { get; set; } [Column("trackCount")]
[Required]
public short TrackCount { get; set; }
public ICollection<Track> Tracks { get; set; } [InverseProperty("ReleaseMedia")]
public virtual ICollection<Track> Tracks { get; set; }
public ReleaseMedia() public ReleaseMedia()
{ {

View file

@ -3,7 +3,6 @@ using Roadie.Library.Utility;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
namespace Roadie.Library.Data namespace Roadie.Library.Data
{ {
@ -106,7 +105,7 @@ namespace Roadie.Library.Data
public override string ToString() public override string ToString()
{ {
return $"Id [{Id}], Status [{ Status }], LibraryStatus [{ LibraryStatus }], Title [{Title}], SortTitle [{ SortTitleValue }], Release Date [{ReleaseYear}]"; return $"Id [{Id}], Status [{ Status }], LibraryStatus [{ LibraryStatus }], Title [{Title}], SortTitle [{ SortTitle }], Release Date [{ReleaseYear}]";
} }
} }
} }

View file

@ -12,9 +12,14 @@ namespace Roadie.Library.Data
[Required] [Required]
public string Description { get; set; } public string Description { get; set; }
[NotMapped] public new bool? IsLocked { get; set; } [NotMapped]
public ApplicationUser User { get; set; } public new bool? IsLocked { get; set; }
[Column("userId")] public int? UserId { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("Requests")]
public virtual User User { get; set; }
[Column("userId")]
public int? UserId { get; set; }
} }
} }

View file

@ -7,23 +7,31 @@ namespace Roadie.Library.Data
[Table("scanHistory")] [Table("scanHistory")]
public class ScanHistory : EntityBase public class ScanHistory : EntityBase
{ {
[Column("forArtistId")] public int? ForArtistId { get; set; } [Column("forArtistId")]
public int? ForArtistId { get; set; }
[Column("forReleaseId")] public int? ForReleaseId { get; set; } [Column("forReleaseId")]
public int? ForReleaseId { get; set; }
[NotMapped] public new bool? IsLocked { get; set; } [NotMapped]
public new bool? IsLocked { get; set; }
[Column("newArtists")] public int? NewArtists { get; set; } [Column("newArtists")]
public int? NewArtists { get; set; }
[Column("newReleases")] public int? NewReleases { get; set; } [Column("newReleases")]
public int? NewReleases { get; set; }
[Column("newTracks")] public int? NewTracks { get; set; } [Column("newTracks")]
public int? NewTracks { get; set; }
[Column("timeSpanInSeconds")] public int TimeSpanInSeconds { get; set; } [Column("timeSpanInSeconds")]
public int TimeSpanInSeconds { get; set; }
public ApplicationUser User { get; set; } public virtual User User { get; set; }
[Column("userId")] public int UserId { get; set; } [Column("userId")]
public int UserId { get; set; }
public ScanHistory() public ScanHistory()
{ {

View file

@ -6,8 +6,11 @@ namespace Roadie.Library.Data
[Table("submission")] [Table("submission")]
public class Submission : EntityBase public class Submission : EntityBase
{ {
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("Submissions")]
public virtual User User { get; set; }
[Column("userId")] public int UserId { get; set; } [Column("userId")]
public int UserId { get; set; }
} }
} }

View file

@ -12,27 +12,48 @@ namespace Roadie.Library.Data
[MaxLength(65535)] [MaxLength(65535)]
public string AlternateNames { get; set; } public string AlternateNames { get; set; }
[Column("amgId")] [MaxLength(50)] public string AmgId { get; set; } [Column("amgId")]
[MaxLength(50)]
public string AmgId { get; set; }
[Column("artistId")] public int? ArtistId { get; set; } [Column("artistId")]
public int? ArtistId { get; set; }
public ICollection<Comment> Comments { get; set; } [ForeignKey(nameof(ArtistId))]
[InverseProperty("Tracks")]
public virtual Artist TrackArtist { get; set; }
[Column("duration")] public int? Duration { get; set; } [InverseProperty("Track")]
public virtual ICollection<Comment> Comments { get; set; }
[Column("fileName")] [MaxLength(500)] public string FileName { get; set; } [Column("duration")]
public int? Duration { get; set; }
[Column("filePath")] [MaxLength(1000)] public string FilePath { get; set; } [Column("fileName")]
[MaxLength(500)]
public string FileName { get; set; }
[Column("fileSize")] public int? FileSize { get; set; } [Column("filePath")]
[MaxLength(1000)]
public string FilePath { get; set; }
[Column("hash")] [MaxLength(32)] public string Hash { get; set; } [Column("fileSize")]
public int? FileSize { get; set; }
[Column("isrc")] [MaxLength(15)] public string ISRC { get; set; } [Column("hash")]
[MaxLength(32)]
public string Hash { get; set; }
[Column("lastFMId")] [MaxLength(50)] public string LastFMId { get; set; } [Column("isrc")]
[MaxLength(15)]
public string ISRC { get; set; }
[Column("lastPlayed")] public DateTime? LastPlayed { get; set; } [Column("lastFMId")]
[MaxLength(50)]
public string LastFMId { get; set; }
[Column("lastPlayed")]
public DateTime? LastPlayed { get; set; }
[Column("musicBrainzId")] [Column("musicBrainzId")]
[MaxLength(100)] [MaxLength(100)]
@ -42,34 +63,53 @@ namespace Roadie.Library.Data
[MaxLength(65535)] [MaxLength(65535)]
public string PartTitles { get; set; } public string PartTitles { get; set; }
[Column("playedCount")] public int? PlayedCount { get; set; } [Column("playedCount")]
public int? PlayedCount { get; set; }
[Column("rating")] public short Rating { get; set; } [Column("rating")]
public short Rating { get; set; }
public ReleaseMedia ReleaseMedia { get; set; } public virtual ReleaseMedia ReleaseMedia { get; set; }
[Column("releaseMediaId")] public int ReleaseMediaId { get; set; } [Column("releaseMediaId")]
public int ReleaseMediaId { get; set; }
[Column("spotifyId")] [MaxLength(100)] public string SpotifyId { get; set; } [Column("spotifyId")]
[MaxLength(100)]
public string SpotifyId { get; set; }
[Column("tags", TypeName = "text")] [Column("tags", TypeName = "text")]
[MaxLength(65535)] [MaxLength(65535)]
public string Tags { get; set; } public string Tags { get; set; }
[Obsolete("Images moved to file system")]
[Column("thumbnail", TypeName = "blob")]
public byte[] Thumbnail { get; set; }
[MaxLength(250)] [MaxLength(250)]
[Column("title")] [Column("title")]
[Required] [Required]
public string Title { get; set; } public string Title { get; set; }
[Column("trackNumber")] [Required] public short TrackNumber { get; set; } [Column("trackNumber")]
[Required]
public short TrackNumber { get; set; }
[InverseProperty("Track")]
public virtual ICollection<Credit> Credits { get; set; }
[InverseProperty("Track")]
public virtual ICollection<PlaylistTrack> Playlists { get; set; }
[InverseProperty("Track")]
public virtual ICollection<UserQue> UserQues { get; set; }
[InverseProperty("Track")]
public virtual ICollection<UserTrack> UserTracks { get; set; }
public Track() public Track()
{ {
Comments = new HashSet<Comment>(); Comments = new HashSet<Comment>();
Credits = new HashSet<Credit>();
Playlists = new HashSet<PlaylistTrack>();
UserQues = new HashSet<UserQue>();
UserTracks = new HashSet<UserTrack>();
Rating = 0; Rating = 0;
} }
} }

View file

@ -5,8 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.IO; using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Roadie.Library.Data namespace Roadie.Library.Data
{ {
@ -38,9 +36,11 @@ namespace Roadie.Library.Data
} }
} }
public Artist TrackArtist { get; set; } /// <summary>
/// This is used when finding metadata, not storedin the database.
[NotMapped] public IEnumerable<string> TrackArtists { get; set; } /// </summary>
[NotMapped]
public virtual IEnumerable<string> TrackArtists { get; set; }
public static string CacheRegionUrn(Guid Id) public static string CacheRegionUrn(Guid Id)
{ {
@ -63,7 +63,7 @@ namespace Roadie.Library.Data
public bool DoesFileForTrackExist(IRoadieSettings configuration) public bool DoesFileForTrackExist(IRoadieSettings configuration)
{ {
var trackPath = PathToTrack(configuration); var trackPath = PathToTrack(configuration);
if(string.IsNullOrEmpty(trackPath)) if (string.IsNullOrEmpty(trackPath))
{ {
return false; return false;
} }

View file

@ -7,19 +7,30 @@ namespace Roadie.Library.Data
[Table("userartist")] [Table("userartist")]
public class UserArtist : EntityBase public class UserArtist : EntityBase
{ {
public Artist Artist { get; set; } [ForeignKey(nameof(ArtistId))]
[InverseProperty("UserArtists")]
public virtual Artist Artist { get; set; }
[Column("artistId")] [Required] public int ArtistId { get; set; } [Column("artistId")]
[Required]
public int ArtistId { get; set; }
[Column("isDisliked")] public bool? IsDisliked { get; set; } [Column("isDisliked")]
public bool? IsDisliked { get; set; }
[Column("isFavorite")] public bool? IsFavorite { get; set; } [Column("isFavorite")]
public bool? IsFavorite { get; set; }
[Column("rating")] public short Rating { get; set; } [Column("rating")]
public short Rating { get; set; }
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("ArtistRatings")]
public virtual User User { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("userId")]
[Required]
public int UserId { get; set; }
public UserArtist() public UserArtist()
{ {

View file

@ -7,13 +7,30 @@ namespace Roadie.Library.Data
[Table("userQue")] [Table("userQue")]
public class UserQue : EntityBase public class UserQue : EntityBase
{ {
[Column("isCurrent")] public bool? IsCurrent { get; set; } [Column("isCurrent")]
[Column("position")] public long? Position { get; set; } public bool? IsCurrent { get; set; }
[Column("queSortOrder")] [Required] public short QueSortOrder { get; set; }
public Track Track { get; set; }
[Column("trackId")] [Required] public int TrackId { get; set; }
public ApplicationUser User { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("position")]
public long? Position { get; set; }
[Column("queSortOrder")]
[Required]
public short QueSortOrder { get; set; }
[ForeignKey(nameof(TrackId))]
[InverseProperty("UserQues")]
public virtual Track Track { get; set; }
[Column("trackId")]
[Required]
public int TrackId { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("UserQues")]
public virtual User User { get; set; }
[Column("userId")]
[Required]
public int UserId { get; set; }
} }
} }

View file

@ -7,19 +7,30 @@ namespace Roadie.Library.Data
[Table("userrelease")] [Table("userrelease")]
public class UserRelease : EntityBase public class UserRelease : EntityBase
{ {
[Column("isDisliked")] public bool? IsDisliked { get; set; } [Column("isDisliked")]
public bool? IsDisliked { get; set; }
[Column("isFavorite")] public bool? IsFavorite { get; set; } [Column("isFavorite")]
public bool? IsFavorite { get; set; }
[Column("rating")] public short Rating { get; set; } [Column("rating")]
public short Rating { get; set; }
public Release Release { get; set; } [ForeignKey(nameof(ReleaseId))]
[InverseProperty("UserRelases")]
public virtual Release Release { get; set; }
[Column("releaseId")] [Required] public int ReleaseId { get; set; } [Column("releaseId")]
[Required]
public int ReleaseId { get; set; }
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("UserReleases")]
public virtual User User { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("userId")]
[Required]
public int UserId { get; set; }
public UserRelease() public UserRelease()
{ {

View file

@ -8,23 +8,36 @@ namespace Roadie.Library.Data
[Table("usertrack")] [Table("usertrack")]
public class UserTrack : EntityBase public class UserTrack : EntityBase
{ {
[Column("isDisliked")] public bool? IsDisliked { get; set; } [Column("isDisliked")]
public bool? IsDisliked { get; set; }
[Column("isFavorite")] public bool? IsFavorite { get; set; } [Column("isFavorite")]
public bool? IsFavorite { get; set; }
[Column("lastPlayed")] public DateTime? LastPlayed { get; set; } [Column("lastPlayed")]
public DateTime? LastPlayed { get; set; }
[Column("playedCount")] public int? PlayedCount { get; set; } [Column("playedCount")]
public int? PlayedCount { get; set; }
[Column("rating")] public short Rating { get; set; } [Column("rating")]
public short Rating { get; set; }
public Track Track { get; set; } [ForeignKey(nameof(TrackId))]
[InverseProperty("UserTracks")]
public virtual Track Track { get; set; }
[Column("trackId")] [Required] public int TrackId { get; set; } [Column("trackId")]
[Required]
public int TrackId { get; set; }
public ApplicationUser User { get; set; } [ForeignKey(nameof(UserId))]
[InverseProperty("UserTracks")]
public virtual User User { get; set; }
[Column("userId")] [Required] public int UserId { get; set; } [Column("userId")]
[Required]
public int UserId { get; set; }
public UserTrack() public UserTrack()
{ {

View file

@ -87,37 +87,38 @@ namespace Roadie.Library.Engines
if (artist.Id < 1 && addArtistResult.Entity.Id > 0) artist.Id = addArtistResult.Entity.Id; if (artist.Id < 1 && addArtistResult.Entity.Id > 0) artist.Id = addArtistResult.Entity.Id;
if (inserted > 0 && artist.Id > 0) if (inserted > 0 && artist.Id > 0)
{ {
if (artistGenreTables != null && artistGenreTables.Any(x => x.GenreId == null)) if (artistGenreTables != null)
{ {
foreach (var artistGenreTable in artistGenreTables) if (artistGenreTables != null)
{ {
var genreName = artistGenreTable.Genre?.Name?.ToAlphanumericName(); foreach (var artistGenreTable in artistGenreTables.Select(x => x.Genre?.Name).Distinct())
if (string.IsNullOrEmpty(genreName)) continue;
if (artistGenreTable.Genre.Name.Length > 100)
{ {
var originalName = artistGenreTable.Genre.Name; var genreName = artistGenreTable.ToAlphanumericName().ToTitleCase();
artistGenreTable.Genre.Name = artistGenreTable.Genre.Name.Substring(0, 99); var normalizedName = genreName.ToUpper();
genreName = genreName.Substring(0, 99); if (string.IsNullOrEmpty(genreName)) continue;
Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{artistGenreTable.Genre.Name}]"); if (genreName.Length > 100)
}
var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == genreName);
if (genre == null)
{
genre = new Genre
{ {
Name = artistGenreTable.Genre.Name, var originalName = genreName;
NormalizedName = genreName genreName = genreName.Substring(0, 99);
}; Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{genreName}]");
DbContext.Genres.Add(genre); }
await DbContext.SaveChangesAsync(); var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == normalizedName);
} if (genre == null)
if (genre != null && genre.Id > 0) {
{ genre = new Genre
{
Name = genreName,
NormalizedName = normalizedName
};
DbContext.Genres.Add(genre);
await DbContext.SaveChangesAsync();
}
DbContext.ArtistGenres.Add(new ArtistGenre DbContext.ArtistGenres.Add(new ArtistGenre
{ {
ArtistId = artist.Id, ArtistId = artist.Id,
GenreId = genre.Id GenreId = genre.Id
}); });
await DbContext.SaveChangesAsync();
} }
} }
} }
@ -157,7 +158,7 @@ namespace Roadie.Library.Engines
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, ex.Serialize()); Logger.LogError(ex, $"Error Adding Artist `{ artist }`, Ex [{ ex.Serialize() }]");
} }
return new OperationResult<Artist> return new OperationResult<Artist>
{ {

View file

@ -110,42 +110,36 @@ namespace Roadie.Library.Engines
if (inserted > 0 && release.Id > 0) if (inserted > 0 && release.Id > 0)
{ {
_addedReleaseIds.Add(release.Id); _addedReleaseIds.Add(release.Id);
if (releaseGenreTables != null && releaseGenreTables.Any(x => x.GenreId == null)) if (releaseGenreTables != null)
{ {
var addedGenreIds = new List<int>(); foreach (var releaseGenreTable in releaseGenreTables.Select(x => x.Genre?.Name).Distinct())
foreach (var releaseGenreTable in releaseGenreTables)
{ {
var genreName = releaseGenreTable.Genre?.Name?.ToAlphanumericName(); var genreName = releaseGenreTable.ToAlphanumericName().ToTitleCase();
var normalizedName = genreName.ToUpper();
if (string.IsNullOrEmpty(genreName)) continue; if (string.IsNullOrEmpty(genreName)) continue;
if (releaseGenreTable.Genre.Name.Length > 100) if (genreName.Length > 100)
{ {
var originalName = releaseGenreTable.Genre.Name; var originalName = genreName;
releaseGenreTable.Genre.Name = releaseGenreTable.Genre.Name.Substring(0, 99);
genreName = genreName.Substring(0, 99); genreName = genreName.Substring(0, 99);
Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{releaseGenreTable.Genre.Name}]"); Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{genreName}]");
} }
var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == genreName); var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == normalizedName);
if (genre == null) if (genre == null)
{ {
genre = new Genre genre = new Genre
{ {
Name = releaseGenreTable.Genre.Name, Name = genreName,
NormalizedName = genreName NormalizedName = normalizedName
}; };
DbContext.Genres.Add(genre); DbContext.Genres.Add(genre);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
} }
if (genre != null && DbContext.ReleaseGenres.Add(new ReleaseGenre
genre.Id > 0 &&
!addedGenreIds.Any(x => x == genre.Id))
{ {
DbContext.ReleaseGenres.Add(new ReleaseGenre ReleaseId = release.Id,
{ GenreId = genre.Id
ReleaseId = release.Id, });
GenreId = genre.Id await DbContext.SaveChangesAsync();
});
addedGenreIds.Add(genre.Id);
}
} }
} }
@ -364,32 +358,6 @@ namespace Roadie.Library.Engines
var release = DatabaseQueryForReleaseTitle(artist, metaData.Release); var release = DatabaseQueryForReleaseTitle(artist, metaData.Release);
//var searchName = metaData.Release.NormalizeName().ToLower();
//var specialSearchName = metaData.Release.ToAlphanumericName();
//var altStart = $"{searchName}|";
//var altIn = $"|{searchName}|";
//var altEnds = $"|{searchName}";
//var altStartSpecial = $"{specialSearchName}|";
//var altInSpecial = $"|{specialSearchName}|";
//var altEndsSpecial = $"|{specialSearchName}";
//var release = (from r in DbContext.Releases
// where r.ArtistId == artist.Id
// where r.Title == searchName ||
// r.Title == specialSearchName ||
// r.AlternateNames == searchName ||
// r.AlternateNames == specialSearchName ||
// r.AlternateNames.Contains(altStart) ||
// r.AlternateNames.Contains(altIn) ||
// r.AlternateNames.Contains(altEnds) ||
// r.AlternateNames.Contains(altStartSpecial) ||
// r.AlternateNames.Contains(altInSpecial) ||
// r.AlternateNames.Contains(altEndsSpecial)
// select r
// ).FirstOrDefault();
sw.Stop(); sw.Stop();
if (release == null || !release.IsValid) if (release == null || !release.IsValid)
{ {

View file

@ -1,41 +0,0 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("userrole")]
public class ApplicationRole : IdentityRole<int>
{
[Column("createdDate")] public DateTime? CreatedDate { get; set; }
[Column("description")]
[StringLength(200)]
public string Description { get; set; }
//[Column("id")]
//[Key]
//public override int Id { get; set; }
[Column("isLocked")] public bool? IsLocked { get; set; }
[Column("lastUpdated")] public DateTime? LastUpdated { get; set; }
[Column("name")]
[Required]
[StringLength(80)]
public override string Name { get; set; }
[Column("RoadieId")]
[StringLength(36)]
public string RoadieId { get; set; }
public virtual ICollection<ApplicationRoleClaim> RoleClaims { get; set; }
[Column("status")] public short? Status { get; set; }
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
}

View file

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("userRoleClaims")]
public class ApplicationRoleClaim : IdentityRoleClaim<int>
{
public virtual ApplicationRole Role { get; set; }
[Column("userRoleId")] public override int RoleId { get; set; }
}
}

View file

@ -1,135 +0,0 @@
using Microsoft.AspNetCore.Identity;
using Roadie.Library.Data;
using Roadie.Library.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
/// <summary>
/// Application User for Identity
/// <remarks>
/// As this is used by UserManager to get for each request in API *do not* lazy load properties as the object
/// is too heavy and requires multiple DB hits to poplate - which is data not needed to authenticate a user.
/// </remarks>
/// </summary>
[Table("user")]
public partial class ApplicationUser : IdentityUser<int>
{
[Column("apiToken")]
[StringLength(100)]
public string ApiToken { get; set; }
public ICollection<UserArtist> ArtistRatings { get; set; }
[Obsolete("Images moved to file system")]
[Column("avatar", TypeName = "blob")]
public byte[] Avatar { get; set; }
public ICollection<Bookmark> Bookmarks { get; set; }
public ICollection<ApplicationUserClaim> Claims { get; set; }
public ICollection<Comment> Comments { get; set; }
[Column("createdDate")] public DateTime? CreatedDate { get; set; }
[Column("doUseHtmlPlayer")] public bool? DoUseHtmlPlayer { get; set; }
[Column("email")]
[Required]
[StringLength(100)]
public override string Email { get; set; }
[Column("ftpDirectory")]
[StringLength(500)]
public string FtpDirectory { get; set; }
[Column("ftpPassword")]
[StringLength(500)]
public string FtpPassword { get; set; }
[Column("ftpUrl")] [StringLength(250)] public string FtpUrl { get; set; }
[Column("ftpUsername")]
[StringLength(50)]
public string FtpUsername { get; set; }
[Column("id")] [Key] public override int Id { get; set; }
[Column("isActive")] public bool? IsActive { get; set; }
[Column("isLocked")] public bool? IsLocked { get; set; }
[Column("isPrivate")] public bool? IsPrivate { get; set; }
/// <summary>
/// This is the last time a user access Roadie via an API (ie Subsonic or Plex or Apache)
/// </summary>
[Column("lastApiAccess")]
public DateTime? LastApiAccess { get; set; }
[Column("lastFMSessionKey")]
[StringLength(50)]
public string LastFMSessionKey { get; set; }
[Column("lastLogin")] public DateTime? LastLogin { get; set; }
[Column("lastUpdated")] public DateTime? LastUpdated { get; set; }
[Column("password")]
[Required]
[StringLength(100)]
public override string PasswordHash { get; set; }
[Column("playerTrackLimit")] public short? PlayerTrackLimit { get; set; }
public ICollection<Playlist> Playlists { get; set; }
[Column("profile", TypeName = "text")]
[StringLength(65535)]
public string Profile { get; set; }
[Column("randomReleaseLimit")] public short? RandomReleaseLimit { get; set; }
[Column("recentlyPlayedLimit")] public short? RecentlyPlayedLimit { get; set; }
[Column("registeredOn")] public DateTime? RegisteredOn { get; set; }
public ICollection<UserRelease> ReleaseRatings { get; set; }
[Column("removeTrackFromQueAfterPlayed")]
public bool? RemoveTrackFromQueAfterPlayed { get; set; }
public ICollection<Request> Requests { get; set; }
[Column("RoadieId")]
[StringLength(36)]
public Guid RoadieId { get; set; }
[Column("status")] public Statuses? Status { get; set; }
public ICollection<Submission> Submissions { get; set; }
[Column("timeformat")]
[StringLength(50)]
public string Timeformat { get; set; }
[Column("timezone")]
[StringLength(50)]
public string Timezone { get; set; }
[Column("defaultRowsPerPage")]
public short? DefaultRowsPerPage { get; set; }
public ICollection<UserTrack> TrackRatings { get; set; }
public ICollection<UserQue> UserQues { get; set; }
public ICollection<ApplicationUserRole> UserRoles { get; set; }
//public ICollection<ChatMessage> ChatMessages { get; set; }
//public ICollection<Collection> Collections { get; set; }
//public ICollection<Submission> Submission { get; set; }
}
}

View file

@ -1,11 +0,0 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("userClaims")]
public class ApplicationUserClaim : IdentityUserClaim<int>
{
public virtual ApplicationUser User { get; set; }
}
}

View file

@ -5,9 +5,9 @@ using Microsoft.EntityFrameworkCore;
namespace Roadie.Library.Identity namespace Roadie.Library.Identity
{ {
public class ApplicationUserDbContext : IdentityDbContext< public class ApplicationUserDbContext : IdentityDbContext<
ApplicationUser, ApplicationRole, int, User, UserRole, int,
ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<int>, UserClaims, UsersInRoles, IdentityUserLogin<int>,
ApplicationRoleClaim, IdentityUserToken<int>> UserRoleClaims, IdentityUserToken<int>>
{ {
public ApplicationUserDbContext(DbContextOptions<ApplicationUserDbContext> options) public ApplicationUserDbContext(DbContextOptions<ApplicationUserDbContext> options)
: base(options) : base(options)
@ -18,46 +18,59 @@ namespace Roadie.Library.Identity
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(b => // The tables are setup in RoadieDBContext but the table mappings below are necessary for IdentityDbContext to work.
builder.Entity<User>(entity =>
{ {
b.ToTable("user"); entity.ToTable("user");
// Each User can have many UserClaims // Each User can have many UserClaims
b.HasMany(e => e.Claims) entity.HasMany(e => e.UserClaims)
.WithOne(e => e.User) .WithOne(e => e.User)
.HasForeignKey(uc => uc.UserId) .HasForeignKey(uc => uc.Id)
.IsRequired(); .IsRequired();
// Each User can have many entries in the UserRole join table // Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles) entity.HasMany(e => e.UserRoles)
.WithOne(e => e.User) .WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId) .HasForeignKey(ur => ur.UserId)
.IsRequired(); .IsRequired();
}); });
builder.Entity<ApplicationRole>(b => builder.Entity<UserClaims>(entity =>
{ {
b.ToTable("userrole"); entity.ToTable("userClaims");
b.HasKey(ar => ar.Id); });
builder.Entity<UserRoleClaims>(entity =>
{
entity.ToTable("userRoleClaims");
});
builder.Entity<UserRole>(entity =>
{
entity.ToTable("userrole");
entity.HasKey(ar => ar.Id);
// Each Role can have many entries in the UserRole join table // Each Role can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles) entity.HasMany(e => e.UserRoles)
.WithOne(e => e.Role) .WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId) .HasForeignKey(ur => ur.RoleId)
.IsRequired(); .IsRequired();
// Each Role can have many associated RoleClaims // Each Role can have many associated RoleClaims
b.HasMany(e => e.RoleClaims) entity.HasMany(e => e.RoleClaims)
.WithOne(e => e.Role) .WithOne(e => e.UserRole)
.HasForeignKey(rc => rc.RoleId) .HasForeignKey(rc => rc.RoleId)
.IsRequired(); .IsRequired();
}); });
builder.Entity<ApplicationUserClaim>(b => { b.ToTable("userClaims"); }); builder.Entity<UsersInRoles>(entity =>
{
builder.Entity<ApplicationUserRole>(b => { b.ToTable("usersInRoles"); }); entity.ToTable("usersInRoles");
});
builder.Entity<ApplicationRoleClaim>(b => { b.ToTable("userRoleClaims"); });
} }
} }
} }

View file

@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("usersInRoles")]
public class ApplicationUserRole : IdentityUserRole<int>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ApplicationRole Role { get; set; }
[Column("userRoleId")] public override int RoleId { get; set; }
public virtual ApplicationUser User { get; set; }
}
}

View file

@ -0,0 +1,168 @@
using Microsoft.AspNetCore.Identity;
using Roadie.Library.Data;
using Roadie.Library.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
/// <summary>
/// Application User for Identity
/// <remarks>
/// As this is used by UserManager to get for each request in API *do not* lazy load properties as the object
/// is too heavy and requires multiple DB hits to poplate - which is data not needed to authenticate a user.
/// </remarks>
/// </summary>
[Table("user")]
public partial class User : IdentityUser<int>
{
[Column("id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Column("apiToken")]
[StringLength(100)]
public string ApiToken { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserArtist> ArtistRatings { get; set; }
[InverseProperty("User")]
public virtual ICollection<Bookmark> Bookmarks { get; set; }
[InverseProperty("User")]
public virtual ICollection<ChatMessage> ChatMessages { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserClaims> UserClaims { get; set; }
[InverseProperty("Maintainer")]
public virtual ICollection<Collection> Collections { get; set; }
[InverseProperty("User")]
public virtual ICollection<Comment> Comments { get; set; }
[InverseProperty("User")]
public virtual ICollection<Data.CommentReaction> CommentReactions { get; set; }
[Column("createdDate")]
public DateTime? CreatedDate { get; set; }
[Column("doUseHtmlPlayer")]
public bool? DoUseHtmlPlayer { get; set; }
[Column("email")]
[Required]
[StringLength(100)]
public override string Email { get; set; }
[Column("ftpDirectory")]
[StringLength(500)]
public string FtpDirectory { get; set; }
[Column("ftpPassword")]
[StringLength(500)]
public string FtpPassword { get; set; }
[Column("ftpUrl")]
[StringLength(250)]
public string FtpUrl { get; set; }
[Column("ftpUsername")]
[StringLength(50)]
public string FtpUsername { get; set; }
[Column("isActive")]
public bool? IsActive { get; set; }
[Column("isLocked")]
public bool? IsLocked { get; set; }
[Column("isPrivate")]
public bool? IsPrivate { get; set; }
/// <summary>
/// This is the last time a user access Roadie via an API (ie Subsonic or Plex or Apache)
/// </summary>
[Column("lastApiAccess")]
public DateTime? LastApiAccess { get; set; }
[Column("lastFMSessionKey")]
[StringLength(50)]
public string LastFMSessionKey { get; set; }
[Column("lastLogin")]
public DateTime? LastLogin { get; set; }
[Column("lastUpdated")]
public DateTime? LastUpdated { get; set; }
[Column("password")]
[Required]
[StringLength(100)]
public override string PasswordHash { get; set; }
[Column("playerTrackLimit")]
public short? PlayerTrackLimit { get; set; }
public virtual ICollection<Playlist> Playlists { get; set; }
[Column("profile", TypeName = "text")]
[StringLength(65535)]
public string Profile { get; set; }
[Column("randomReleaseLimit")]
public short? RandomReleaseLimit { get; set; }
[Column("recentlyPlayedLimit")]
public short? RecentlyPlayedLimit { get; set; }
[Column("registeredOn")]
public DateTime? RegisteredOn { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserRelease> UserReleases { get; set; }
[Column("removeTrackFromQueAfterPlayed")]
public bool? RemoveTrackFromQueAfterPlayed { get; set; }
[InverseProperty("CreatedByUser")]
public virtual ICollection<InviteToken> InviteTokens { get; set; }
[InverseProperty("User")]
public virtual ICollection<Request> Requests { get; set; }
[Column("RoadieId")]
[StringLength(36)]
public Guid RoadieId { get; set; }
[Column("status")]
public Statuses? Status { get; set; }
[InverseProperty("User")]
public virtual ICollection<Submission> Submissions { get; set; }
[Column("timeformat")]
[StringLength(50)]
public string Timeformat { get; set; }
[Column("timezone")]
[StringLength(50)]
public string Timezone { get; set; }
[Column("defaultRowsPerPage")]
public short? DefaultRowsPerPage { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserTrack> UserTracks { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserQue> UserQues { get; set; }
[InverseProperty("User")]
public virtual ICollection<UsersInRoles> UserRoles { get; set; }
}
}

View file

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("userClaims")]
public class UserClaims : IdentityUserClaim<int>
{
[Column("id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Required]
[Column("userId")]
public override int UserId { get; set; }
[Required]
[Column("claimType")]
[StringLength(200)]
public override string ClaimType { get; set; }
[Required]
[Column("claimValue")]
[StringLength(200)]
public override string ClaimValue { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("UserClaims")]
public virtual User User { get; set; }
}
}

View file

@ -4,12 +4,11 @@ using Roadie.Library.Enums;
using Roadie.Library.Extensions; using Roadie.Library.Extensions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
namespace Roadie.Library.Identity namespace Roadie.Library.Identity
{ {
public partial class ApplicationUser public partial class User
{ {
public string CacheKey => CacheUrn(RoadieId); public string CacheKey => CacheUrn(RoadieId);
@ -30,7 +29,7 @@ namespace Roadie.Library.Identity
return Path.Combine(folder, $"{ UserName.ToFileNameFriendly() } [{ Id }].gif"); return Path.Combine(folder, $"{ UserName.ToFileNameFriendly() } [{ Id }].gif");
} }
public ApplicationUser() public User()
{ {
RoadieId = Guid.NewGuid(); RoadieId = Guid.NewGuid();
Status = Statuses.Ok; Status = Statuses.Ok;
@ -45,16 +44,20 @@ namespace Roadie.Library.Identity
RecentlyPlayedLimit = 20; RecentlyPlayedLimit = 20;
RandomReleaseLimit = 20; RandomReleaseLimit = 20;
// Collections = new HashSet<Collection>(); ArtistRatings = new HashSet<UserArtist>();
Bookmarks = new HashSet<Bookmark>();
Collections = new HashSet<Collection>();
Comments = new HashSet<Comment>();
Playlists = new HashSet<Playlist>(); Playlists = new HashSet<Playlist>();
UserRoles = new HashSet<ApplicationUserRole>(); CommentReactions = new HashSet<Data.CommentReaction>();
InviteTokens = new HashSet<InviteToken>();
UserReleases = new HashSet<UserRelease>();
Requests = new HashSet<Request>(); Requests = new HashSet<Request>();
Submissions = new HashSet<Submission>(); Submissions = new HashSet<Submission>();
UserTracks = new HashSet<UserTrack>();
UserClaims = new HashSet<UserClaims>();
UserQues = new HashSet<UserQue>(); UserQues = new HashSet<UserQue>();
ArtistRatings = new HashSet<UserArtist>(); UserRoles = new HashSet<UsersInRoles>();
ReleaseRatings = new HashSet<UserRelease>();
TrackRatings = new HashSet<UserTrack>();
Comments = new HashSet<Comment>();
} }
public static string CacheRegionUrn(Guid Id) public static string CacheRegionUrn(Guid Id)

View file

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
/// <summary>
/// Definition of a User Role
/// </summary>
[Table("userrole")]
public class UserRole : IdentityRole<int>
{
[Column("id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Column("createdDate")]
public DateTime? CreatedDate { get; set; }
[Column("description")]
[StringLength(200)]
public string Description { get; set; }
[Column("isLocked")]
public bool? IsLocked { get; set; }
[Column("lastUpdated")]
public DateTime? LastUpdated { get; set; }
[Column("name")]
[Required]
[StringLength(80)]
public override string Name { get; set; }
[Column("RoadieId")]
[StringLength(36)]
public string RoadieId { get; set; }
[InverseProperty("UserRole")]
public virtual ICollection<UserRoleClaims> RoleClaims { get; set; }
[Column("status")]
public short? Status { get; set; }
[InverseProperty("Role")]
public virtual ICollection<UsersInRoles> UserRoles { get; set; }
}
}

View file

@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
[Table("userRoleClaims")]
public class UserRoleClaims : IdentityRoleClaim<int>
{
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Column("userRoleId")]
public override int RoleId { get; set; }
[Required]
[Column("claimType", TypeName = "varchar(200)")]
public new string ClaimType { get; set; }
[Required]
[Column("claimValue", TypeName = "varchar(200)")]
public new string ClaimValue { get; set; }
[ForeignKey(nameof(RoleId))]
[InverseProperty("RoleClaims")]
public virtual UserRole UserRole { get; set; }
}
}

View file

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Roadie.Library.Identity
{
/// <summary>
/// Users In Roles
/// </summary>
[Table("usersInRoles")]
public class UsersInRoles : IdentityUserRole<int>
{
[Column("id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey(nameof(RoleId))]
[InverseProperty("UserRoles")]
public virtual UserRole Role { get; set; }
[Column("userRoleId")]
[Required]
public override int RoleId { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("UserRoles")]
public virtual User User { get; set; }
[Column("userId")]
[Required]
public override int UserId { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using Roadie.Library.Enums; using Roadie.Library.Enums;
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
namespace Roadie.Library.Imaging namespace Roadie.Library.Imaging
@ -25,7 +26,15 @@ namespace Roadie.Library.Imaging
{ {
return null; return null;
} }
return ImageHasher.AverageHash(Bytes).ToString(); try
{
return ImageHasher.AverageHash(Bytes).ToString();
}
catch (Exception ex)
{
Trace.WriteLine($"Error Generating Signature Ex [{ ex }]", "Warning");
}
return null;
} }
public Image() public Image()

View file

@ -49,7 +49,7 @@ namespace Roadie.Library.Imaging
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]"); Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]", "Warning");
} }
return null; return null;
} }
@ -74,7 +74,7 @@ namespace Roadie.Library.Imaging
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]"); Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]", "Warning");
} }
return null; return null;
} }
@ -293,7 +293,7 @@ namespace Roadie.Library.Imaging
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Resizing Image [{ex}]"); Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning");
} }
return null; return null;
} }
@ -312,7 +312,7 @@ namespace Roadie.Library.Imaging
var result = ImageHelper.ResizeImage(ImageHelper.ConvertToJpegFormat(imageBytes), width, height, true)?.Item2; var result = ImageHelper.ResizeImage(ImageHelper.ConvertToJpegFormat(imageBytes), width, height, true)?.Item2;
if (result?.Length >= ImageHelper.MaximumThumbnailByteSize) if (result?.Length >= ImageHelper.MaximumThumbnailByteSize)
{ {
Trace.WriteLine($"Thumbnail larger than maximum size after resizing to [{configuration.ThumbnailImageSize.Width}x{configuration.ThumbnailImageSize.Height}] Thumbnail Size [{result.Length}]"); Trace.WriteLine($"Thumbnail larger than maximum size after resizing to [{configuration.ThumbnailImageSize.Width}x{configuration.ThumbnailImageSize.Height}] Thumbnail Size [{result.Length}]", "Warning");
result = new byte[0]; result = new byte[0];
} }
return result; return result;

View file

@ -86,7 +86,7 @@ namespace Roadie.Library.Models
[MaxLength(65535)] public string Profile { get; set; } [MaxLength(65535)] public string Profile { get; set; }
public decimal? Rank { get; set; } public double? Rank { get; set; }
/// <summary> /// <summary>
/// The Position of this Artist as ranked against other Artists (highest ranking Artist is #1) /// The Position of this Artist as ranked against other Artists (highest ranking Artist is #1)

View file

@ -1,5 +1,6 @@
using Roadie.Library.Enums; using Roadie.Library.Enums;
using Roadie.Library.Models.Users; using Roadie.Library.Models.Users;
using Roadie.Library.Utility;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -15,7 +16,7 @@ namespace Roadie.Library.Models
public DateTime? LastPlayed { get; set; } public DateTime? LastPlayed { get; set; }
public IEnumerable<string> MissingReleasesForCollection { get; set; } public IEnumerable<string> MissingReleasesForCollection { get; set; }
public int? PlayedCount { get; set; } public int? PlayedCount { get; set; }
public decimal? Rank { get; set; } public double? Rank { get; set; }
public short? Rating { get; set; } public short? Rating { get; set; }
public int? ReleaseCount { get; set; } public int? ReleaseCount { get; set; }
public Image Thumbnail { get; set; } public Image Thumbnail { get; set; }
@ -37,7 +38,7 @@ namespace Roadie.Library.Models
}, },
Thumbnail = thumbnail, Thumbnail = thumbnail,
Rating = artist.Rating, Rating = artist.Rating,
Rank = artist.Rank, Rank = SafeParser.ToNumber<double?>(artist.Rank),
CreatedDate = artist.CreatedDate, CreatedDate = artist.CreatedDate,
LastUpdated = artist.LastUpdated, LastUpdated = artist.LastUpdated,
LastPlayed = artist.LastPlayed, LastPlayed = artist.LastPlayed,

View file

@ -19,7 +19,10 @@ namespace Roadie.Library.Models.Playlists
{ {
get get
{ {
if (!Duration.HasValue) return "--:--"; if (!Duration.HasValue)
{
return "--:--";
}
return new TimeInfo(Duration.Value).ToFullFormattedString(); return new TimeInfo(Duration.Value).ToFullFormattedString();
} }
} }

View file

@ -26,7 +26,7 @@ namespace Roadie.Library.Models.Playlists
public DataToken User { get; set; } public DataToken User { get; set; }
public Image UserThumbnail { get; set; } public Image UserThumbnail { get; set; }
public static PlaylistList FromDataPlaylist(Data.Playlist playlist, ApplicationUser user, Image playlistThumbnail, Image userThumbnail) public static PlaylistList FromDataPlaylist(Data.Playlist playlist, User user, Image playlistThumbnail, Image userThumbnail)
{ {
return new PlaylistList return new PlaylistList
{ {

View file

@ -58,7 +58,7 @@ namespace Roadie.Library.Models.Releases
[MaxLength(65535)] public string Profile { get; set; } [MaxLength(65535)] public string Profile { get; set; }
public decimal? Rank { get; set; } public double? Rank { get; set; }
/// <summary> /// <summary>
/// The Position of this Release as ranked against other Releases (highest ranking Release is #1) /// The Position of this Release as ranked against other Releases (highest ranking Release is #1)

View file

@ -50,7 +50,7 @@ namespace Roadie.Library.Models.Releases
public IEnumerable<ReleaseMediaList> Media { get; set; } public IEnumerable<ReleaseMediaList> Media { get; set; }
public int? MediaCount { get; set; } public int? MediaCount { get; set; }
public decimal? Rank { get; set; } public double? Rank { get; set; }
public short? Rating { get; set; } public short? Rating { get; set; }
public DataToken Release { get; set; } public DataToken Release { get; set; }
@ -98,7 +98,7 @@ namespace Roadie.Library.Models.Releases
LibraryStatus = release.LibraryStatus, LibraryStatus = release.LibraryStatus,
MediaCount = release.MediaCount, MediaCount = release.MediaCount,
Rating = release.Rating, Rating = release.Rating,
Rank = release.Rank, Rank = SafeParser.ToNumber<double?>(release.Rank),
ReleaseDateDateTime = release.ReleaseDate, ReleaseDateDateTime = release.ReleaseDate,
ReleasePlayUrl = $"{baseUrl}/play/release/{release.RoadieId}", ReleasePlayUrl = $"{baseUrl}/play/release/{release.RoadieId}",
Status = release.Status, Status = release.Status,

View file

@ -4,6 +4,6 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
{ {
public class SubsonicAuthenticateResponse : Response public class SubsonicAuthenticateResponse : Response
{ {
public ApplicationUser User { get; set; } public Identity.User SubsonicUser { get; set; }
} }
} }

View file

@ -20,7 +20,7 @@ namespace Roadie.Library.Models.Users
public Image Thumbnail { get; set; } public Image Thumbnail { get; set; }
public DataToken User { get; set; } public DataToken User { get; set; }
public static UserList FromDataUser(ApplicationUser user, Image thumbnail) public static UserList FromDataUser(Identity.User user, Image thumbnail)
{ {
return new UserList return new UserList
{ {

View file

@ -13,7 +13,7 @@
<PackageReference Include="EFCore.BulkExtensions" Version="3.0.3" /> <PackageReference Include="EFCore.BulkExtensions" Version="3.0.3" />
<PackageReference Include="FluentFTP" Version="28.0.2" /> <PackageReference Include="FluentFTP" Version="28.0.2" />
<PackageReference Include="Hashids.net" Version="1.3.0" /> <PackageReference Include="Hashids.net" Version="1.3.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.16" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.17" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" /> <PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" /> <PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" /> <PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
@ -22,13 +22,13 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="1.3.0" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="1.3.0" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" /> <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.3" /> <PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.3" />
<PackageReference Include="MimeMapping" Version="1.0.1.17" /> <PackageReference Include="MimeMapping" Version="1.0.1.17" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="2.4.7" /> <PackageReference Include="NodaTime" Version="2.4.7" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0" />
<PackageReference Include="RestSharp" Version="106.6.10" /> <PackageReference Include="RestSharp" Version="106.6.10" />
@ -39,7 +39,7 @@
<PackageReference Include="System.Drawing.Common" Version="4.6.1" /> <PackageReference Include="System.Drawing.Common" Version="4.6.1" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.6.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.6.0" />
<PackageReference Include="System.Runtime.Caching" Version="4.6.0" /> <PackageReference Include="System.Runtime.Caching" Version="4.6.0" />
<PackageReference Include="z440.atl.core" Version="2.13.0" /> <PackageReference Include="z440.atl.core" Version="2.14.0" />
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.4" /> <PackageReference Include="zlib.net-mutliplatform" Version="1.0.4" />
</ItemGroup> </ItemGroup>

View file

@ -63,13 +63,13 @@ namespace Roadie.Library.MetaData.MusicBrainz
var response = ex.Response as HttpWebResponse; var response = ex.Response as HttpWebResponse;
if(response?.StatusCode == HttpStatusCode.NotFound) if(response?.StatusCode == HttpStatusCode.NotFound)
{ {
Trace.WriteLine($"GetAsync: 404 Response For url [{ url }]"); Trace.WriteLine($"GetAsync: 404 Response For url [{ url }]", "Warning");
return result; return result;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"GetAsync: [{ ex.ToString() }]"); Trace.WriteLine($"GetAsync: [{ ex.ToString() }]", "Warning");
Thread.Sleep(100); Thread.Sleep(100);
} }
finally finally

View file

@ -33,7 +33,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Reading Cue [{ cueFilename }] [{ ex }]"); Trace.WriteLine($"Error Reading Cue [{ cueFilename }] [{ ex }]", "Error");
} }
return results; return results;
} }
@ -70,7 +70,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Reading Sfv [{ sfvFilename }] [{ ex }]"); Trace.WriteLine($"Error Reading Sfv [{ sfvFilename }] [{ ex }]", "Error");
} }
return results; return results;
} }
@ -101,7 +101,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error Reading Cue [{ m3uFilename }] [{ ex }]"); Trace.WriteLine($"Error Reading Cue [{ m3uFilename }] [{ ex }]", "Error");
} }
return results; return results;
} }

View file

@ -169,18 +169,6 @@ namespace Roadie.Library.Utility
return directoryInfo.FullName; return directoryInfo.FullName;
} }
[Obsolete("This is only here for migration will be removed in future release.")]
public static string ArtistPathOld(IRoadieSettings configuration, string artistSortName)
{
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistSortName),"Invalid Artist Sort Name");
var artistFolder = artistSortName.ToTitleCase(false);
var directoryInfo = new DirectoryInfo(Path.Combine(configuration.LibraryFolder, artistFolder.ToFolderNameFriendly()));
return directoryInfo.FullName;
}
/// <summary> /// <summary>
/// Full path to Release folder using given full Artist folder /// Full path to Release folder using given full Artist folder
/// </summary> /// </summary>
@ -222,17 +210,6 @@ namespace Roadie.Library.Utility
return directoryInfo.FullName; return directoryInfo.FullName;
} }
[Obsolete("This is only here for migration will be removed in future release.")]
public static string ReleasePathOld(string artistFolder, string releaseTitle, DateTime releaseDate)
{
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistFolder), "Invalid Artist Folder");
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(releaseTitle), "Invalid Release Title");
SimpleContract.Requires<ArgumentException>(releaseDate != DateTime.MinValue, "Invalid Release Date");
var directoryInfo = new DirectoryInfo(Path.Combine(artistFolder, string.Format("{1}{0}", releaseTitle.ToTitleCase(false).ToFolderNameFriendly(), string.Format("[{0}] ", releaseDate.ToString("yyyy")))));
return directoryInfo.FullName;
}
/// <summary> /// <summary>
/// Delete any empty folders in the given folder /// Delete any empty folders in the given folder
/// </summary> /// </summary>
@ -260,7 +237,7 @@ namespace Roadie.Library.Utility
if (!folder.GetFiles("*.*", SearchOption.AllDirectories).Any()) if (!folder.GetFiles("*.*", SearchOption.AllDirectories).Any())
{ {
folder.Delete(true); folder.Delete(true);
Trace.WriteLine($"Deleting Empty Folder [{folder.FullName}]", "Debug"); Trace.WriteLine($"Deleting Empty Folder [{folder.FullName}]", "Warning");
result = true; result = true;
} }
} }
@ -268,24 +245,24 @@ namespace Roadie.Library.Utility
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
{ {
result = false; result = false;
Trace.WriteLine($"UnauthorizedAccessException Deleting Empty Folder [{folder.FullName}]", "Debug"); Trace.WriteLine($"UnauthorizedAccessException Deleting Empty Folder [{folder.FullName}]", "Warning");
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {
result = false; result = false;
Trace.WriteLine($"DirectoryNotFoundException Deleting Empty Folder [{folder.FullName}]", "Debug"); Trace.WriteLine($"DirectoryNotFoundException Deleting Empty Folder [{folder.FullName}]", "Warning");
} }
} }
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
{ {
result = false; result = false;
Trace.WriteLine($"UnauthorizedAccessException Deleting Empty Folder [{processingFolder.FullName}]", "Debug"); Trace.WriteLine($"UnauthorizedAccessException Deleting Empty Folder [{processingFolder.FullName}]", "Warning");
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {
result = false; result = false;
Trace.WriteLine($"DirectoryNotFoundException Deleting Empty Folder [{processingFolder.FullName}]", "Debug"); Trace.WriteLine($"DirectoryNotFoundException Deleting Empty Folder [{processingFolder.FullName}]", "Warning");
} }
return result; return result;
} }

View file

@ -52,7 +52,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine(string.Format("Error with url [{0}] Exception [{1}]", url, ex)); Trace.WriteLine(string.Format("Error with url [{0}] Exception [{1}]", url, ex), "Warning");
} }
return null; return null;
@ -90,7 +90,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"GetImageFromUrlAsync Url [{ url }], Exception [{ ex.ToString() }"); Trace.WriteLine($"GetImageFromUrlAsync Url [{ url }], Exception [{ ex.ToString() }", "Warning");
} }
return null; return null;
} }
@ -104,7 +104,7 @@ namespace Roadie.Library.Utility
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine(ex.ToString()); Trace.WriteLine(ex.ToString(), "Error");
} }
return false; return false;

View file

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity; using Mapster;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -40,6 +41,7 @@ namespace Roadie.Api.Services
private ILabelService LabelService { get; } private ILabelService LabelService { get; }
private IArtistLookupEngine ArtistLookupEngine { get; } private IArtistLookupEngine ArtistLookupEngine { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; } private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IBookmarkService BookmarkService { get; }
private IReleaseService ReleaseService { get; } private IReleaseService ReleaseService { get; }
@ -47,7 +49,7 @@ namespace Roadie.Api.Services
IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger, IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger,
IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService, IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService,
IReleaseService releaseService, IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine, IReleaseService releaseService, IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine,
ILabelService labelService, IGenreService genreService ILabelService labelService, IGenreService genreService, IBookmarkService bookmarkService
) )
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
{ {
@ -62,9 +64,10 @@ namespace Roadie.Api.Services
ArtistLookupEngine = artistLookupEngine; ArtistLookupEngine = artistLookupEngine;
ReleaseLookupEngine = releaseLookupEngine; ReleaseLookupEngine = releaseLookupEngine;
FileDirectoryProcessorService = fileDirectoryProcessorService; FileDirectoryProcessorService = fileDirectoryProcessorService;
BookmarkService = bookmarkService;
} }
public async Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId, bool deleteFolder) public async Task<OperationResult<bool>> DeleteArtist(User user, Guid artistId, bool deleteFolder)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -105,7 +108,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false) public async Task<OperationResult<bool>> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -140,7 +143,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index) public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(User user, Guid artistId, int index)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -181,7 +184,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteGenre(ApplicationUser user, Guid genreId) public async Task<OperationResult<bool>> DeleteGenre(User user, Guid genreId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -205,7 +208,7 @@ namespace Roadie.Api.Services
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information); await LogAndPublish($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information);
return new OperationResult<bool> return new OperationResult<bool>
{ {
@ -216,7 +219,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId) public async Task<OperationResult<bool>> DeleteLabel(User user, Guid labelId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -251,7 +254,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles) public async Task<OperationResult<bool>> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -266,7 +269,6 @@ namespace Roadie.Api.Services
await LogAndPublish($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning); await LogAndPublish($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]"); return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
} }
await ReleaseService.Delete(user, release, doDeleteFiles ?? false); await ReleaseService.Delete(user, release, doDeleteFiles ?? false);
} }
catch (Exception ex) catch (Exception ex)
@ -288,7 +290,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index) public async Task<OperationResult<bool>> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -329,7 +331,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteTracks(ApplicationUser user, IEnumerable<Guid> trackIds, bool? doDeleteFile) public async Task<OperationResult<bool>> DeleteTracks(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -376,8 +378,8 @@ namespace Roadie.Api.Services
Logger.LogError(ex, string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, track.Id, ex.Serialize())); Logger.LogError(ex, string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, track.Id, ex.Serialize()));
} }
} }
await ReleaseService.ScanReleaseFolder(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release); await ReleaseService.ScanReleaseFolder(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release);
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Track, track.Id);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -399,7 +401,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteUser(ApplicationUser applicationUser, Guid userId) public async Task<OperationResult<bool>> DeleteUser(User applicationUser, Guid userId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -452,20 +454,31 @@ namespace Roadie.Api.Services
/// <summary> /// <summary>
/// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers /// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers
/// </summary> /// </summary>
public async Task<OperationResult<bool>> DoInitialSetup(ApplicationUser user, UserManager<ApplicationUser> userManager) public async Task<OperationResult<bool>> DoInitialSetup(User user, UserManager<User> userManager)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
// Create user roles // Create user roles
DbContext.UserRoles.Add(new ApplicationRole DbContext.UserRoles.Add(new UserRole
{ {
Id = 1,
Status = (short)Statuses.Ok,
CreatedDate = DateTime.UtcNow,
IsLocked = false,
RoadieId = Guid.NewGuid().ToString(),
Name = "Admin", Name = "Admin",
Description = "Users with Administrative (full) access", Description = "Users with Administrative (full) access",
NormalizedName = "ADMIN" NormalizedName = "ADMIN"
}); });
DbContext.UserRoles.Add(new ApplicationRole
DbContext.UserRoles.Add(new UserRole
{ {
Id = 2,
Status = (short)Statuses.Ok,
CreatedDate = DateTime.UtcNow,
IsLocked = false,
RoadieId = Guid.NewGuid().ToString(),
Name = "Editor", Name = "Editor",
Description = "Users who have Edit Permissions", Description = "Users who have Edit Permissions",
NormalizedName = "EDITOR" NormalizedName = "EDITOR"
@ -512,7 +525,7 @@ namespace Roadie.Api.Services
}; };
} }
public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user) public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(User user)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
@ -606,6 +619,14 @@ namespace Roadie.Api.Services
Directory.CreateDirectory(Configuration.LabelImageFolder); Directory.CreateDirectory(Configuration.LabelImageFolder);
Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]"); Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]");
} }
if (Configuration.DbContextToUse != DbContexts.MySQL)
{
if (!Directory.Exists(Configuration.FileDatabaseOptions.DatabaseFolder))
{
Directory.CreateDirectory(Configuration.FileDatabaseOptions.DatabaseFolder);
Logger.LogInformation($"Created File Database Folder [{Configuration.FileDatabaseOptions.DatabaseFolder}]");
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -619,14 +640,14 @@ namespace Roadie.Api.Services
Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]"); Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]");
} }
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, public async Task<OperationResult<bool>> ScanAllCollections(User user, bool isReadOnly = false,
bool doPurgeFirst = false) bool doPurgeFirst = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var errors = new List<Exception>(); var errors = new List<Exception>();
var collections = DbContext.Collections.Where(x => x.IsLocked == false).ToArray(); var collections = await DbContext.Collections.Where(x => x.IsLocked == false).ToArrayAsync();
var updatedReleaseIds = new List<int>(); var updatedReleaseIds = new List<int>();
foreach (var collection in collections) foreach (var collection in collections)
try try
@ -659,7 +680,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanArtist(User user, Guid artistId, bool isReadOnly = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
@ -702,7 +723,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanArtists(ApplicationUser user, IEnumerable<Guid> artistIds, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanArtists(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
@ -728,7 +749,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) public async Task<OperationResult<bool>> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -749,7 +770,7 @@ namespace Roadie.Api.Services
if (doPurgeFirst) if (doPurgeFirst)
{ {
await LogAndPublish($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning); await LogAndPublish($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning);
var crs = DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray(); var crs = await DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArrayAsync();
DbContext.CollectionReleases.RemoveRange(crs); DbContext.CollectionReleases.RemoveRange(crs);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
} }
@ -907,19 +928,19 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanInboundFolder(User user, bool isReadOnly = false)
{ {
var d = new DirectoryInfo(Configuration.InboundFolder); var d = new DirectoryInfo(Configuration.InboundFolder);
return await ScanFolder(user, d, isReadOnly); return await ScanFolder(user, d, isReadOnly);
} }
public async Task<OperationResult<bool>> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanLibraryFolder(User user, bool isReadOnly = false)
{ {
var d = new DirectoryInfo(Configuration.LibraryFolder); var d = new DirectoryInfo(Configuration.LibraryFolder);
return await ScanFolder(user, d, isReadOnly, false); return await ScanFolder(user, d, isReadOnly, false);
} }
public async Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) public async Task<OperationResult<bool>> ScanRelease(User user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -964,7 +985,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanReleases(ApplicationUser user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) public async Task<OperationResult<bool>> ScanReleases(User user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
@ -1080,7 +1101,7 @@ namespace Roadie.Api.Services
await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message); await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message);
} }
private async Task<OperationResult<bool>> ScanFolder(ApplicationUser user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true) private async Task<OperationResult<bool>> ScanFolder(User user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();

View file

@ -71,7 +71,7 @@ namespace Roadie.Api.Services
FileDirectoryProcessorService = fileDirectoryProcessorService; FileDirectoryProcessorService = fileDirectoryProcessorService;
} }
public async Task<OperationResult<Artist>> ById(User roadieUser, Guid id, IEnumerable<string> includes) public async Task<OperationResult<Artist>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
var tsw = new Stopwatch(); var tsw = new Stopwatch();
@ -94,11 +94,10 @@ namespace Roadie.Api.Services
tsw.Stop(); tsw.Stop();
timings.Add("getArtist", tsw.ElapsedMilliseconds); timings.Add("getArtist", tsw.ElapsedMilliseconds);
tsw.Restart(); tsw.Restart();
var userBookmarkResult = var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist);
await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist);
if (userBookmarkResult.IsSuccess) if (userBookmarkResult.IsSuccess)
{ {
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == artist.RoadieId.ToString()) != null; result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == artist?.RoadieId.ToString()) != null;
} }
tsw.Stop(); tsw.Stop();
timings.Add("userBookmarkResult", tsw.ElapsedMilliseconds); timings.Add("userBookmarkResult", tsw.ElapsedMilliseconds);
@ -148,7 +147,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> Delete(ApplicationUser user, data.Artist artist, bool deleteFolder) public async Task<OperationResult<bool>> Delete(Library.Identity.User user, data.Artist artist, bool deleteFolder)
{ {
var isSuccess = false; var isSuccess = false;
try try
@ -159,9 +158,25 @@ namespace Roadie.Api.Services
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
if(deleteFolder) if(deleteFolder)
{ {
// Delete all image files for Artist
foreach (var file in ImageHelper.ImageFilesInFolder(artist.ArtistFileFolder(Configuration), SearchOption.TopDirectoryOnly))
{
try
{
File.Delete(file);
Logger.LogWarning("For Artist [{0}], Deleted File [{1}]", artist.Id, file);
}
catch (Exception ex)
{
Logger.LogError(ex, $"Error Deleting File [{file}] Exception [{ex}]");
}
}
var artistDir = new DirectoryInfo(artist.ArtistFileFolder(Configuration)); var artistDir = new DirectoryInfo(artist.ArtistFileFolder(Configuration));
FolderPathHelper.DeleteEmptyFolders(artistDir.Parent); FolderPathHelper.DeleteEmptyFolders(artistDir.Parent);
} }
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Artist, artist.Id);
await UpdatePlaylistCountsForArtist(artist.Id, DateTime.UtcNow);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
Logger.LogWarning("User `{0}` deleted Artist `{1}]`", user, artist); Logger.LogWarning("User `{0}` deleted Artist `{1}]`", user, artist);
isSuccess = true; isSuccess = true;
@ -183,7 +198,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true) public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -287,7 +302,7 @@ namespace Roadie.Api.Services
}, },
Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId),
Rating = a.Rating, Rating = a.Rating,
Rank = a.Rank, Rank = (double?)a.Rank,
CreatedDate = a.CreatedDate, CreatedDate = a.CreatedDate,
LastUpdated = a.LastUpdated, LastUpdated = a.LastUpdated,
LastPlayed = a.LastPlayed, LastPlayed = a.LastPlayed,
@ -313,7 +328,7 @@ namespace Roadie.Api.Services
else else
{ {
string sortBy; string sortBy;
if (request.ActionValue == User.ActionKeyUserRated) if (request.ActionValue == Library.Models.Users.User.ActionKeyUserRated)
{ {
sortBy = string.IsNullOrEmpty(request.Sort) sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Rating", "DESC" }, { "Artist.Text", "ASC" } }) ? request.OrderValue(new Dictionary<string, string> { { "Rating", "DESC" }, { "Artist.Text", "ASC" } })
@ -380,7 +395,7 @@ namespace Roadie.Api.Services
/// <param name="artistToMerge">The Artist to be merged</param> /// <param name="artistToMerge">The Artist to be merged</param>
/// <param name="artistToMergeInto">The Artist to merge into</param> /// <param name="artistToMergeInto">The Artist to merge into</param>
/// <returns></returns> /// <returns></returns>
public async Task<OperationResult<bool>> MergeArtists(ApplicationUser user, Guid artistToMergeId, Guid artistToMergeIntoId) public async Task<OperationResult<bool>> MergeArtists(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -434,7 +449,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid artistId) public async Task<OperationResult<bool>> RefreshArtistMetadata(Library.Identity.User user, Guid artistId)
{ {
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId"); SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
@ -498,7 +513,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo) public async Task<OperationResult<bool>> ScanArtistReleasesFolders(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo)
{ {
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId"); SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
@ -581,12 +596,12 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Library.Models.Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl) public async Task<OperationResult<Library.Models.Image>> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl)
{ {
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
} }
public async Task<OperationResult<bool>> UpdateArtist(ApplicationUser user, Artist model) public async Task<OperationResult<bool>> UpdateArtist(Library.Identity.User user, Artist model)
{ {
var didRenameArtist = false; var didRenameArtist = false;
var sw = new Stopwatch(); var sw = new Stopwatch();
@ -853,7 +868,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Library.Models.Image>> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file) public async Task<OperationResult<Library.Models.Image>> UploadArtistImage(Library.Identity.User user, Guid id, IFormFile file)
{ {
var bytes = new byte[0]; var bytes = new byte[0];
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
@ -890,7 +905,7 @@ namespace Roadie.Api.Services
? null ? null
: result.BirthDate; : result.BirthDate;
result.RankPosition = result.Rank > 0 result.RankPosition = result.Rank > 0
? SafeParser.ToNumber<int?>(DbContext.Artists.Count(x => x.Rank > result.Rank) + 1) ? SafeParser.ToNumber<int?>(DbContext.Artists.Count(x => (double?)x.Rank > result.Rank) + 1)
: null; : null;
tsw.Stop(); tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds); timings.Add("adapt", tsw.ElapsedMilliseconds);
@ -1044,7 +1059,7 @@ namespace Roadie.Api.Services
}, },
Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId),
Rating = a.Rating, Rating = a.Rating,
Rank = a.Rank, Rank = (double?)a.Rank,
CreatedDate = a.CreatedDate, CreatedDate = a.CreatedDate,
LastUpdated = a.LastUpdated, LastUpdated = a.LastUpdated,
LastPlayed = a.LastPlayed, LastPlayed = a.LastPlayed,
@ -1068,7 +1083,7 @@ namespace Roadie.Api.Services
}, },
Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId),
Rating = a.Rating, Rating = a.Rating,
Rank = a.Rank, Rank = (double?)a.Rank,
CreatedDate = a.CreatedDate, CreatedDate = a.CreatedDate,
LastUpdated = a.LastUpdated, LastUpdated = a.LastUpdated,
LastPlayed = a.LastPlayed, LastPlayed = a.LastPlayed,
@ -1100,7 +1115,7 @@ namespace Roadie.Api.Services
}, },
Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId),
Rating = a.Rating, Rating = a.Rating,
Rank = a.Rank, Rank = (double?)a.Rank,
CreatedDate = a.CreatedDate, CreatedDate = a.CreatedDate,
LastUpdated = a.LastUpdated, LastUpdated = a.LastUpdated,
LastPlayed = a.LastPlayed, LastPlayed = a.LastPlayed,
@ -1124,7 +1139,7 @@ namespace Roadie.Api.Services
}, },
Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId),
Rating = a.Rating, Rating = a.Rating,
Rank = a.Rank, Rank = (double?)a.Rank,
CreatedDate = a.CreatedDate, CreatedDate = a.CreatedDate,
LastUpdated = a.LastUpdated, LastUpdated = a.LastUpdated,
LastPlayed = a.LastPlayed, LastPlayed = a.LastPlayed,
@ -1283,7 +1298,7 @@ namespace Roadie.Api.Services
}; };
} }
private async Task<OperationResult<data.Artist>> MergeArtists(ApplicationUser user, data.Artist artistToMerge, data.Artist artistToMergeInto) private async Task<OperationResult<data.Artist>> MergeArtists(Library.Identity.User user, data.Artist artistToMerge, data.Artist artistToMergeInto)
{ {
SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist"); SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist");
SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist"); SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist");
@ -1437,7 +1452,7 @@ namespace Roadie.Api.Services
}; };
} }
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes) private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(Library.Identity.User user, Guid id, byte[] imageBytes)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();

View file

@ -1,5 +1,7 @@
using Microsoft.EntityFrameworkCore; using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Roadie.Library;
using Roadie.Library.Caching; using Roadie.Library.Caching;
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
using Roadie.Library.Data.Context; using Roadie.Library.Data.Context;
@ -25,14 +27,41 @@ namespace Roadie.Api.Services
{ {
public class BookmarkService : ServiceBase, IBookmarkService public class BookmarkService : ServiceBase, IBookmarkService
{ {
private IUserService UserService { get; }
public BookmarkService(IRoadieSettings configuration, public BookmarkService(IRoadieSettings configuration,
IHttpEncoder httpEncoder, IHttpEncoder httpEncoder,
IHttpContext httpContext, IHttpContext httpContext,
IRoadieDbContext context, IRoadieDbContext context,
ICacheManager cacheManager, ICacheManager cacheManager,
IUserService userService,
ILogger<BookmarkService> logger) ILogger<BookmarkService> logger)
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
{ {
UserService = userService;
}
/// <summary>
/// When a bookmarkable item gets deleted then delete any bookmarks to that item, since its a generic column there is not FK setup.
/// </summary>
public async Task<OperationResult<bool>> RemoveAllBookmarksForItem(BookmarkType type, int id)
{
var bookmarks = await DbContext.Bookmarks.Include(x => x.User)
.Where(x => x.BookmarkType == type && x.BookmarkTargetId == id)
.ToListAsync();
var users = bookmarks.Select(x => x.User).ToList().Distinct();
DbContext.Bookmarks.RemoveRange(bookmarks);
await DbContext.SaveChangesAsync();
foreach(var user in users)
{
CacheManager.ClearRegion(user.CacheRegion);
}
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
} }
public async Task<Library.Models.Pagination.PagedResult<models.BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null) public async Task<Library.Models.Pagination.PagedResult<models.BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null)

View file

@ -77,8 +77,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Collection>> ById(User roadieUser, Guid id, public async Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
IEnumerable<string> includes = null)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
@ -94,7 +93,7 @@ namespace Roadie.Api.Services
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection); var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection);
if (userBookmarkResult.IsSuccess) if (userBookmarkResult.IsSuccess)
{ {
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == result.Data.Id.ToString()) != null; result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
} }
if (result.Data.Comments.Any()) if (result.Data.Comments.Any())
{ {
@ -142,6 +141,7 @@ namespace Roadie.Api.Services
{ {
DbContext.Collections.Remove(collection); DbContext.Collections.Remove(collection);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Collection, collection.Id);
var collectionImageFilename = collection.PathToImage(Configuration); var collectionImageFilename = collection.PathToImage(Configuration);
if (File.Exists(collectionImageFilename)) if (File.Exists(collectionImageFilename))
{ {
@ -277,7 +277,7 @@ namespace Roadie.Api.Services
} }
collection.IsLocked = model.IsLocked; collection.IsLocked = model.IsLocked;
var oldPathToImage = collection.PathToImage(Configuration); var oldPathToImage = collection.PathToImage(Configuration);
var didChangeName = collection.Name != model.Name; var didChangeName = collection.Name != model.Name && !isNew;
collection.Name = model.Name; collection.Name = model.Name;
collection.SortName = model.SortName; collection.SortName = model.SortName;
collection.Edition = model.Edition; collection.Edition = model.Edition;
@ -292,6 +292,21 @@ namespace Roadie.Api.Services
collection.AlternateNames = model.AlternateNamesList.ToDelimitedList(); collection.AlternateNames = model.AlternateNamesList.ToDelimitedList();
collection.CollectionCount = model.CollectionCount; collection.CollectionCount = model.CollectionCount;
if (model.Maintainer?.Value != null)
{
var maintainer = DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value));
if (maintainer != null)
{
collection.MaintainerId = maintainer.Id;
}
}
if (isNew)
{
await DbContext.Collections.AddAsync(collection);
await DbContext.SaveChangesAsync();
}
if (didChangeName) if (didChangeName)
{ {
if (File.Exists(oldPathToImage)) if (File.Exists(oldPathToImage))
@ -306,18 +321,12 @@ namespace Roadie.Api.Services
// Save unaltered collection image // Save unaltered collection image
File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage)); File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage));
} }
if (model.Maintainer?.Value != null)
{
var maintainer = DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value));
if (maintainer != null) collection.MaintainerId = maintainer.Id;
}
collection.LastUpdated = now; collection.LastUpdated = now;
if (isNew) await DbContext.Collections.AddAsync(collection);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(collection.CacheRegion); CacheManager.ClearRegion(collection.CacheRegion);
Logger.LogInformation($"UpdateArtist `{collection}` By User `{user}`"); Logger.LogInformation($"UpdateCollection `{collection}` By User `{user}`");
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = !errors.Any(),

View file

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Identity.UI.Services;
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Mail; using System.Net.Mail;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,6 +18,11 @@ namespace Roadie.Api.Services
public async Task SendEmailAsync(string email, string subject, string htmlMessage) public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{ {
if(string.IsNullOrEmpty(Configuration.SmtpHost))
{
Trace.WriteLine("Email Server (Configuration.SmtpHost) Not Configured", "Warning");
return;
}
using (var mail = new MailMessage(Configuration.SmtpFromAddress, email)) using (var mail = new MailMessage(Configuration.SmtpFromAddress, email))
{ {
using (var client = new SmtpClient()) using (var client = new SmtpClient())

View file

@ -76,7 +76,7 @@ namespace Roadie.Api.Services
return result; return result;
} }
public async Task<OperationResult<bool>> Process(ApplicationUser user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true) public async Task<OperationResult<bool>> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -121,7 +121,7 @@ namespace Roadie.Api.Services
/// <summary> /// <summary>
/// Perform any operations to the given folder and the plugin results after processing /// Perform any operations to the given folder and the plugin results after processing
/// </summary> /// </summary>
private async Task<bool> PostProcessFolder(ApplicationUser user, DirectoryInfo inboundFolder, IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo, bool doDeleteFiles = true) private async Task<bool> PostProcessFolder(User user, DirectoryInfo inboundFolder, IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo, bool doDeleteFiles = true)
{ {
SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder"); SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder");
if (pluginResults != null) if (pluginResults != null)

View file

@ -38,7 +38,7 @@ namespace Roadie.Api.Services
{ {
} }
public async Task<OperationResult<Genre>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null) public async Task<OperationResult<Genre>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
@ -58,7 +58,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id) public async Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -84,7 +84,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false) public async Task<Library.Models.Pagination.PagedResult<GenreList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -161,12 +161,12 @@ namespace Roadie.Api.Services
}); });
} }
public async Task<OperationResult<Library.Models.Image>> SetGenreImageByUrl(User user, Guid id, string imageUrl) public async Task<OperationResult<Library.Models.Image>> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl)
{ {
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
} }
public async Task<OperationResult<bool>> UpdateGenre(User user, Genre model) public async Task<OperationResult<bool>> UpdateGenre(Library.Models.Users.User user, Genre model)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -243,7 +243,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Library.Models.Image>> UploadGenreImage(User user, Guid id, IFormFile file) public async Task<OperationResult<Library.Models.Image>> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file)
{ {
var bytes = new byte[0]; var bytes = new byte[0];
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
@ -309,7 +309,7 @@ namespace Roadie.Api.Services
}); });
} }
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes) private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();

View file

@ -9,45 +9,45 @@ namespace Roadie.Api.Services
{ {
public interface IAdminService public interface IAdminService
{ {
Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId, bool deleteFolder); Task<OperationResult<bool>> DeleteArtist(User user, Guid artistId, bool deleteFolder);
Task<OperationResult<bool>> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false); Task<OperationResult<bool>> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false);
Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index); Task<OperationResult<bool>> DeleteArtistSecondaryImage(User user, Guid artistId, int index);
Task<OperationResult<bool>> DeleteGenre(ApplicationUser user, Guid genreId); Task<OperationResult<bool>> DeleteGenre(User user, Guid genreId);
Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId); Task<OperationResult<bool>> DeleteLabel(User user, Guid labelId);
Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles); Task<OperationResult<bool>> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles);
Task<OperationResult<bool>> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index); Task<OperationResult<bool>> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index);
Task<OperationResult<bool>> DeleteTracks(ApplicationUser user, IEnumerable<Guid> trackIds, bool? doDeleteFile); Task<OperationResult<bool>> DeleteTracks(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile);
Task<OperationResult<bool>> DeleteUser(ApplicationUser applicationUser, Guid id); Task<OperationResult<bool>> DeleteUser(User applicationUser, Guid id);
Task<OperationResult<bool>> DoInitialSetup(ApplicationUser user, UserManager<ApplicationUser> userManager); Task<OperationResult<bool>> DoInitialSetup(User user, UserManager<User> userManager);
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user); Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(User user);
void PerformStartUpTasks(); void PerformStartUpTasks();
Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false); Task<OperationResult<bool>> ScanAllCollections(User user, bool isReadOnly = false, bool doPurgeFirst = false);
Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false); Task<OperationResult<bool>> ScanArtist(User user, Guid artistId, bool isReadOnly = false);
Task<OperationResult<bool>> ScanArtists(ApplicationUser user, IEnumerable<Guid> artistIds, bool isReadOnly = false); Task<OperationResult<bool>> ScanArtists(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false);
Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); Task<OperationResult<bool>> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true);
Task<OperationResult<bool>> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false); Task<OperationResult<bool>> ScanInboundFolder(User user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false); Task<OperationResult<bool>> ScanLibraryFolder(User user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); Task<OperationResult<bool>> ScanRelease(User user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<bool>> ScanReleases(ApplicationUser user, IEnumerable<Guid> releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); Task<OperationResult<bool>> ScanReleases(User user, IEnumerable<Guid> releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId); Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId);

View file

@ -12,22 +12,22 @@ namespace Roadie.Api.Services
{ {
public interface IArtistService public interface IArtistService
{ {
Task<OperationResult<Artist>> ById(User user, Guid id, IEnumerable<string> includes); Task<OperationResult<Artist>> ById(Library.Models.Users.User user, Guid id, IEnumerable<string> includes);
Task<OperationResult<bool>> Delete(ApplicationUser user, Library.Data.Artist Artist, bool deleteFolder); Task<OperationResult<bool>> Delete(Library.Identity.User user, Library.Data.Artist Artist, bool deleteFolder);
Task<PagedResult<ArtistList>> List(User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true); Task<PagedResult<ArtistList>> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true);
Task<OperationResult<bool>> MergeArtists(ApplicationUser user, Guid artistToMergeId, Guid artistToMergeIntoId); Task<OperationResult<bool>> MergeArtists(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId);
Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid ArtistId); Task<OperationResult<bool>> RefreshArtistMetadata(Library.Identity.User user, Guid ArtistId);
Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo); Task<OperationResult<bool>> ScanArtistReleasesFolders(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo);
Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl); Task<OperationResult<Image>> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateArtist(ApplicationUser user, Artist artist); Task<OperationResult<bool>> UpdateArtist(Library.Identity.User user, Artist artist);
Task<OperationResult<Image>> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadArtistImage(Library.Identity.User user, Guid id, IFormFile file);
} }
} }

View file

@ -1,4 +1,5 @@
using Roadie.Library.Enums; using Roadie.Library;
using Roadie.Library.Enums;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users; using Roadie.Library.Models.Users;
@ -8,7 +9,7 @@ namespace Roadie.Api.Services
{ {
public interface IBookmarkService public interface IBookmarkService
{ {
Task<PagedResult<BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Task<OperationResult<bool>> RemoveAllBookmarksForItem(BookmarkType type, int id);
BookmarkType? filterType = null); Task<PagedResult<BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null);
} }
} }

View file

@ -14,6 +14,6 @@ namespace Roadie.Api.Services
int? ProcessLimit { get; set; } int? ProcessLimit { get; set; }
Task<OperationResult<bool>> Process(ApplicationUser user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true); Task<OperationResult<bool>> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true);
} }
} }

View file

@ -12,16 +12,16 @@ namespace Roadie.Api.Services
{ {
public interface IGenreService public interface IGenreService
{ {
Task<OperationResult<Genre>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Genre>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id); Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id);
Task<PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false); Task<PagedResult<GenreList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<Image>> SetGenreImageByUrl(User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateGenre(User user, Genre model); Task<OperationResult<bool>> UpdateGenre(Library.Models.Users.User user, Genre model);
Task<OperationResult<Image>> UploadGenreImage(User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file);
} }
} }

View file

@ -12,18 +12,18 @@ namespace Roadie.Api.Services
{ {
public interface ILabelService public interface ILabelService
{ {
Task<OperationResult<Label>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Label>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id); Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id);
Task<PagedResult<LabelList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false); Task<PagedResult<LabelList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<bool>> MergeLabelsIntoLabel(ApplicationUser user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge); Task<OperationResult<bool>> MergeLabelsIntoLabel(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge);
Task<OperationResult<Image>> SetLabelImageByUrl(User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetLabelImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateLabel(User user, Label label); Task<OperationResult<bool>> UpdateLabel(Library.Models.Users.User user, Label label);
Task<OperationResult<Image>> UploadLabelImage(User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadLabelImage(Library.Models.Users.User user, Guid id, IFormFile file);
} }
} }

View file

@ -15,26 +15,26 @@ namespace Roadie.Api.Services
{ {
IEnumerable<int> AddedTrackIds { get; } IEnumerable<int> AddedTrackIds { get; }
Task<OperationResult<Release>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Release>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(ApplicationUser user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true); Task<OperationResult<bool>> Delete(Library.Identity.User user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true);
Task<OperationResult<bool>> DeleteReleases(ApplicationUser user, IEnumerable<Guid> releaseIds, bool doDeleteFiles = false); Task<OperationResult<bool>> DeleteReleases(Library.Identity.User user, IEnumerable<Guid> releaseIds, bool doDeleteFiles = false);
Task<PagedResult<ReleaseList>> List(User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null); Task<PagedResult<ReleaseList>> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null);
Task<OperationResult<bool>> MergeReleases(ApplicationUser user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia); Task<OperationResult<bool>> MergeReleases(Library.Identity.User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia);
Task<OperationResult<bool>> MergeReleases(ApplicationUser user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia); Task<OperationResult<bool>> MergeReleases(Library.Identity.User user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia);
Task<FileOperationResult<byte[]>> ReleaseZipped(User roadieUser, Guid id); Task<FileOperationResult<byte[]>> ReleaseZipped(Library.Models.Users.User roadieUser, Guid id);
Task<OperationResult<bool>> ScanReleaseFolder(ApplicationUser user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null); Task<OperationResult<bool>> ScanReleaseFolder(Library.Identity.User user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null);
Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl); Task<OperationResult<Image>> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateRelease(ApplicationUser user, Release release, string originalReleaseFolder = null); Task<OperationResult<bool>> UpdateRelease(Library.Identity.User user, Release release, string originalReleaseFolder = null);
Task<OperationResult<Image>> UploadReleaseImage(ApplicationUser user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadReleaseImage(Library.Identity.User user, Guid id, IFormFile file);
} }
} }

View file

@ -6,6 +6,6 @@ namespace Roadie.Api.Services
{ {
public interface ITokenService public interface ITokenService
{ {
Task<string> GenerateToken(ApplicationUser user, UserManager<ApplicationUser> userManager); Task<string> GenerateToken(User user, UserManager<User> userManager);
} }
} }

View file

@ -178,7 +178,7 @@ namespace Roadie.Api.Services
public async Task<FileOperationResult<IImage>> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) public async Task<FileOperationResult<IImage>> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{ {
return await GetImageFileOperation("UserById", return await GetImageFileOperation("UserById",
ApplicationUser.CacheRegionUrn(id), User.CacheRegionUrn(id),
id, id,
width, width,
height, height,
@ -206,7 +206,7 @@ namespace Roadie.Api.Services
artistFolder = artist.ArtistFileFolder(Configuration); artistFolder = artist.ArtistFileFolder(Configuration);
if (!Directory.Exists(artistFolder)) if (!Directory.Exists(artistFolder))
{ {
Logger.LogTrace($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`"); Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`");
} }
else else
{ {
@ -257,7 +257,7 @@ namespace Roadie.Api.Services
artistFolder = artist.ArtistFileFolder(Configuration); artistFolder = artist.ArtistFileFolder(Configuration);
if (!Directory.Exists(artistFolder)) if (!Directory.Exists(artistFolder))
{ {
Logger.LogTrace($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`"); Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`");
} }
else else
{ {
@ -545,7 +545,7 @@ namespace Roadie.Api.Services
artistFolder = release.Artist.ArtistFileFolder(Configuration); artistFolder = release.Artist.ArtistFileFolder(Configuration);
if (!Directory.Exists(artistFolder)) if (!Directory.Exists(artistFolder))
{ {
Logger.LogTrace($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`"); Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`");
} }
else else
{ {
@ -605,7 +605,7 @@ namespace Roadie.Api.Services
artistFolder = release.Artist.ArtistFileFolder(Configuration); artistFolder = release.Artist.ArtistFileFolder(Configuration);
if (!Directory.Exists(artistFolder)) if (!Directory.Exists(artistFolder))
{ {
Logger.LogTrace($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`"); Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`");
} }
else else
{ {

View file

@ -43,7 +43,7 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
} }
public async Task<OperationResult<Label>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null) public async Task<OperationResult<Label>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
@ -57,7 +57,7 @@ namespace Roadie.Api.Services
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label); var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label);
if (userBookmarkResult.IsSuccess) if (userBookmarkResult.IsSuccess)
{ {
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == result.Data.Id.ToString()) != null; result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
} }
if (result.Data.Comments.Any()) if (result.Data.Comments.Any())
{ {
@ -85,7 +85,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id) public async Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -99,7 +99,7 @@ namespace Roadie.Api.Services
{ {
File.Delete(labelImageFilename); File.Delete(labelImageFilename);
} }
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Label, label.Id);
Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label); Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label);
CacheManager.ClearRegion(label.CacheRegion); CacheManager.ClearRegion(label.CacheRegion);
sw.Stop(); sw.Stop();
@ -111,7 +111,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<Library.Models.Pagination.PagedResult<LabelList>> List(User roadieUser, PagedRequest request, public async Task<Library.Models.Pagination.PagedResult<LabelList>> List(Library.Models.Users.User roadieUser, PagedRequest request,
bool? doRandomize = false) bool? doRandomize = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
@ -198,7 +198,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> MergeLabelsIntoLabel(ApplicationUser user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge) public async Task<OperationResult<bool>> MergeLabelsIntoLabel(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -258,12 +258,12 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Library.Models.Image>> SetLabelImageByUrl(User user, Guid id, string imageUrl) public async Task<OperationResult<Library.Models.Image>> SetLabelImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl)
{ {
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
} }
public async Task<OperationResult<bool>> UpdateLabel(User user, Label model) public async Task<OperationResult<bool>> UpdateLabel(Library.Models.Users.User user, Label model)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -344,7 +344,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<Library.Models.Image>> UploadLabelImage(User user, Guid id, IFormFile file) public async Task<OperationResult<Library.Models.Image>> UploadLabelImage(Library.Models.Users.User user, Guid id, IFormFile file)
{ {
var bytes = new byte[0]; var bytes = new byte[0];
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
@ -452,7 +452,7 @@ namespace Roadie.Api.Services
}; };
} }
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes) private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();

Some files were not shown because too many files have changed in this diff Show more