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;
@ -14,7 +15,8 @@ namespace Roadie.Library.Data
[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)]
@ -35,6 +37,14 @@ namespace Roadie.Library.Data
[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,9 +130,10 @@ 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()) while (csv.Read())
{ {
index++; index++;
@ -143,6 +146,7 @@ namespace Roadie.Library.Data
}); });
} }
} }
}
_positionArtistReleases = rows; _positionArtistReleases = rows;
} }
@ -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

@ -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,46 +79,115 @@ 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(),
// v => string.IsNullOrEmpty(v) ? Statuses.Ok : (Statuses)Enum.Parse(typeof(Statuses), v))
// .HasDefaultValue(Statuses.Ok);
//builder
// .Entity<Artist>()
// .Property(e => e.Status)
// .HasConversion(
// v => v.ToString(),
// v => string.IsNullOrEmpty(v) ? Statuses.Incomplete : (Statuses)Enum.Parse(typeof(Statuses), v))
// .HasDefaultValue(Statuses.Incomplete);
builder
.Entity<Release>()
.Property(e => e.ReleaseType)
.HasConversion( .HasConversion(
v => v.ToString(), v => v.ToString(),
v => string.IsNullOrEmpty(v) v => string.IsNullOrEmpty(v) ? BandStatus.Unknown : (BandStatus)Enum.Parse(typeof(BandStatus), v))
? ReleaseType.Unknown .HasDefaultValue(BandStatus.Unknown);
: (ReleaseType)Enum.Parse(typeof(ReleaseType), v))
.HasDefaultValue(ReleaseType.Release);
builder entity.HasIndex(e => e.Name)
.Entity<Release>() .HasName("ix_artist_name")
.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 entity.HasIndex(e => e.RoadieId)
.Entity<Collection>() .HasName("ix_artist_roadieId");
entity.HasIndex(e => e.SortName)
.HasName("ix_artist_sortname")
.IsUnique();
});
builder.Entity<ArtistAssociation>(entity =>
{
entity.HasIndex(e => e.AssociatedArtistId)
.HasName("ix_associatedArtistId");
entity.HasIndex(e => new { e.ArtistId, e.AssociatedArtistId })
.HasName("ix__artistAssociation");
entity.HasOne(d => d.Artist)
.WithMany(p => p.AssociatedArtists)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("artistAssociation_ibfk_1");
});
builder.Entity<ArtistGenre>(entity =>
{
entity.HasIndex(e => e.ArtistId)
.HasName("ix_artistGenreTable_artistId");
entity.HasIndex(e => e.GenreId)
.HasName("ix_artistGenre_genreId");
entity.HasIndex(e => new { e.ArtistId, e.GenreId })
.HasName("ix__artistGenreAssociation");
entity.HasOne(d => d.Artist)
.WithMany(p => p.Genres)
.HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("artistGenreTable_ibfk_1");
entity.HasOne(d => d.Genre)
.WithMany(p => p.Artists)
.HasForeignKey(d => d.GenreId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("artistGenreTable_ibfk_2");
});
builder.Entity<ArtistSimilar>(entity =>
{
entity.HasIndex(e => e.SimilarArtistId)
.HasName("ix_similarArtistId");
entity.HasIndex(e => new { e.ArtistId, e.SimilarArtistId })
.HasName("ix_artistSimilar");
entity.HasOne(d => d.Artist)
.WithMany(p => p.SimilarArtists)
.HasForeignKey(d => d.ArtistId)
.HasConstraintName("artistSimilar_ibfk_1");
});
builder.Entity<Bookmark>(entity =>
{
entity.HasIndex(e => e.RoadieId)
.HasName("ix_bookmark_roadieId");
entity.HasIndex(e => e.UserId)
.HasName("ix_bookmark_userId");
entity.HasIndex(e => new { e.BookmarkType, e.BookmarkTargetId, e.UserId })
.HasName("ix_bookmark_bookmarkType")
.IsUnique();
entity.HasOne(d => d.User)
.WithMany(p => p.Bookmarks)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("bookmark_ibfk_1");
});
builder.Entity<ChatMessage>(entity =>
{
entity.HasIndex(e => e.UserId)
.HasName("ix__chatMessage_user");
entity.HasOne(d => d.User)
.WithMany(p => p.ChatMessages)
.HasForeignKey(d => d.UserId)
.HasConstraintName("chatMessage_ibfk_1");
});
builder.Entity<Collection>(entity =>
{
entity
.Property(e => e.CollectionType) .Property(e => e.CollectionType)
.HasConversion( .HasConversion(
v => v.ToString(), v => v.ToString(),
@ -127,94 +196,620 @@ namespace Roadie.Library.Data.Context
: (CollectionType)Enum.Parse(typeof(CollectionType), v)) : (CollectionType)Enum.Parse(typeof(CollectionType), v))
.HasDefaultValue(CollectionType.Unknown); .HasDefaultValue(CollectionType.Unknown);
builder entity.HasIndex(e => e.MaintainerId)
.Entity<Artist>() .HasName("ix_collection_maintainerId");
.Property(e => e.BandStatus)
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( .HasConversion(
v => v.ToString(), v => v.ToString(),
v => string.IsNullOrEmpty(v) ? BandStatus.Unknown : (BandStatus)Enum.Parse(typeof(BandStatus), v)) v => string.IsNullOrEmpty(v)
.HasDefaultValue(BandStatus.Unknown); ? ReleaseType.Unknown
: (ReleaseType)Enum.Parse(typeof(ReleaseType), v))
.HasDefaultValue(ReleaseType.Release);
//builder entity
// .Entity<Bookmark>() .Property(e => e.LibraryStatus)
// .Property(e => e.BookmarkType) .HasConversion(
// .HasConversion( v => v.ToString(),
// v => v.ToString(), v => string.IsNullOrEmpty(v)
// v => string.IsNullOrEmpty(v) ? BookmarkType.Unknown : (BookmarkType)Enum.Parse(typeof(BookmarkType), v)) ? LibraryStatus.Incomplete
// .HasDefaultValue(BookmarkType.Unknown); : (LibraryStatus)Enum.Parse(typeof(LibraryStatus), v))
.HasDefaultValue(LibraryStatus.Incomplete);
builder.Entity<Release>() entity.HasIndex(e => e.RoadieId)
.HasOne(d => d.Artist) .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) .WithMany(p => p.Releases)
.HasForeignKey(d => d.ArtistId) .HasForeignKey(d => d.ArtistId)
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("release_ibfk_1"); .HasConstraintName("release_ibfk_1");
});
builder.Entity<ReleaseLabel>() builder.Entity<ReleaseGenre>(entity =>
.HasOne(rl => rl.Release) {
.WithMany(r => r.Labels) entity.HasIndex(e => e.GenreId)
.HasForeignKey(rl => rl.ReleaseId); .HasName("ix_releaseGenre_genreId");
builder.Entity<ReleaseLabel>() entity.HasIndex(e => new { e.ReleaseId, e.GenreId })
.HasOne(rl => rl.Label) .HasName("ix_releaseGenreTableReleaseAndGenre");
.WithMany(l => l.ReleaseLabels)
.HasForeignKey(rl => rl.LabelId);
builder.Entity<ReleaseMedia>() entity.HasOne(d => d.Genre)
.HasMany(rm => rm.Tracks) .WithMany(p => p.Releases)
.WithOne(t => t.ReleaseMedia) .HasForeignKey(d => d.GenreId)
.HasForeignKey(rm => rm.ReleaseMediaId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaseGenreTable_ibfk_2");
builder.Entity<ReleaseMedia>() entity.HasOne(d => d.Release)
.HasOne(rm => rm.Release) .WithMany(p => p.Genres)
.WithMany(r => r.Medias) .HasForeignKey(d => d.ReleaseId)
.HasForeignKey(r => r.ReleaseId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaseGenreTable_ibfk_1");
});
builder.Entity<ReleaseGenre>() builder.Entity<ReleaseLabel>(entity =>
.HasKey(rg => new { rg.ReleaseId, rg.GenreId }); {
entity.HasIndex(e => e.LabelId)
.HasName("ix_releaselabel_labelId");
builder.Entity<ReleaseGenre>() entity.HasIndex(e => e.RoadieId)
.HasOne(rg => rg.Release) .HasName("ix_releaselabel_roadieId");
.WithMany(r => r.Genres)
.HasForeignKey(rg => rg.ReleaseId);
builder.Entity<ReleaseGenre>() entity.HasIndex(e => new { e.ReleaseId, e.LabelId })
.HasOne(rg => rg.Genre) .HasName("ix_release_label");
.WithMany(g => g.Releases)
.HasForeignKey(rg => rg.GenreId);
builder.Entity<ArtistGenre>() entity.HasOne(d => d.Label)
.HasKey(rg => new { rg.ArtistId, rg.GenreId }); .WithMany(p => p.ReleaseLabels)
.HasForeignKey(d => d.LabelId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaselabel_ibfk_2");
builder.Entity<ArtistGenre>() entity.HasOne(d => d.Release)
.HasOne(rg => rg.Artist) .WithMany(p => p.Labels)
.WithMany(r => r.Genres) .HasForeignKey(d => d.ReleaseId)
.HasForeignKey(rg => rg.ArtistId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releaselabel_ibfk_1");
});
builder.Entity<ArtistGenre>() builder.Entity<ReleaseMedia>(entity =>
.HasOne(rg => rg.Genre) {
.WithMany(g => g.Artists) entity.HasIndex(e => e.RoadieId)
.HasForeignKey(rg => rg.GenreId); .HasName("ix_releasemedia_roadieId");
builder.Entity<CollectionRelease>() entity.HasIndex(e => new { e.ReleaseId, e.MediaNumber })
.HasOne(cr => cr.Release) .HasName("ix_releasemedia_releaseId");
.WithMany(r => r.Collections)
.HasForeignKey(cr => cr.ReleaseId);
builder.Entity<CollectionRelease>() entity.HasOne(d => d.Release)
.HasOne(cr => cr.Collection) .WithMany(p => p.Medias)
.WithMany(c => c.Releases) .HasForeignKey(d => d.ReleaseId)
.HasForeignKey(cr => cr.CollectionId); .OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("releasemedia_ibfk_1");
});
builder.Entity<Bookmark>() builder.Entity<Request>(entity =>
.HasOne(b => b.User) {
.WithMany(u => u.Bookmarks) entity.HasIndex(e => e.RoadieId)
.HasForeignKey(b => b.UserId); .HasName("ix_request_roadieId");
//builder.Entity<Track>() entity.HasIndex(e => e.UserId)
// .HasOne(t => t.TrackArtist) .HasName("ix_requestartist_ibfk_1");
// .WithMany(a => a.Tracks)
// .HasForeignKey(t => t.ArtistId); 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]

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,9 +7,11 @@ 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)]
@ -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

@ -33,16 +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.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; }

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

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)
{ {

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())
{
var genreName = artistGenreTable.ToAlphanumericName().ToTitleCase();
var normalizedName = genreName.ToUpper();
if (string.IsNullOrEmpty(genreName)) continue; if (string.IsNullOrEmpty(genreName)) continue;
if (artistGenreTable.Genre.Name.Length > 100) if (genreName.Length > 100)
{ {
var originalName = artistGenreTable.Genre.Name; var originalName = genreName;
artistGenreTable.Genre.Name = artistGenreTable.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 [{artistGenreTable.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 = artistGenreTable.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 && genre.Id > 0)
{
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 &&
genre.Id > 0 &&
!addedGenreIds.Any(x => x == genre.Id))
{
DbContext.ReleaseGenres.Add(new ReleaseGenre DbContext.ReleaseGenres.Add(new ReleaseGenre
{ {
ReleaseId = release.Id, ReleaseId = release.Id,
GenreId = genre.Id GenreId = genre.Id
}); });
addedGenreIds.Add(genre.Id); await DbContext.SaveChangesAsync();
}
} }
} }
@ -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,8 +26,16 @@ namespace Roadie.Library.Imaging
{ {
return null; return null;
} }
try
{
return ImageHasher.AverageHash(Bytes).ToString(); 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();
@ -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();

View file

@ -134,7 +134,7 @@ namespace Roadie.Api.Services
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist); var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
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())
{ {
@ -182,6 +182,7 @@ namespace Roadie.Api.Services
DbContext.Playlists.Remove(playlist); DbContext.Playlists.Remove(playlist);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync();
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Playlist, playlist.Id);
var playlistImageFilename = playlist.PathToImage(Configuration); var playlistImageFilename = playlist.PathToImage(Configuration);
if (File.Exists(playlistImageFilename)) if (File.Exists(playlistImageFilename))

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