using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.InteropServices; using System.Text; using API.Schema.MangaConnectors; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using static System.IO.UnixFileMode; namespace API.Schema; [PrimaryKey("MangaId")] public class Manga { [StringLength(64)] [Required] public string MangaId { get; init; } [StringLength(256)] [Required] public string IdOnConnectorSite { get; init; } [StringLength(512)] [Required] public string Name { get; internal set; } [Required] public string Description { get; internal set; } [Url] [StringLength(512)] [Required] public string WebsiteUrl { get; internal init; } [JsonIgnore] [Url] [StringLength(512)] public string CoverUrl { get; internal set; } [Required] public MangaReleaseStatus ReleaseStatus { get; internal set; } [StringLength(64)] public string? LibraryId { get; init; } [JsonIgnore] public LocalLibrary? Library { get; internal set; } [StringLength(32)] [Required] public string MangaConnectorName { get; init; } [JsonIgnore] public MangaConnector MangaConnector { get; init; } = null!; public ICollection Authors { get; internal set; }= null!; public ICollection MangaTags { get; internal set; }= null!; public ICollection Links { get; internal set; }= null!; public ICollection AltTitles { get; internal set; } = null!; [Required] public float IgnoreChaptersBefore { get; internal set; } [StringLength(1024)] [Required] public string DirectoryName { get; private set; } [JsonIgnore] [StringLength(512)] public string? CoverFileNameInCache { get; internal set; } = null; public uint? Year { get; internal init; } [StringLength(8)] public string? OriginalLanguage { get; internal init; } [JsonIgnore] [NotMapped] public string? FullDirectoryPath => Library is not null ? Path.Join(Library.BasePath, DirectoryName) : null; [JsonIgnore] public ICollection Chapters { get; internal set; } = []; public Manga(string idOnConnector, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, MangaConnector mangaConnector, ICollection authors, ICollection mangaTags, ICollection links, ICollection altTitles, LocalLibrary? library = null, float ignoreChaptersBefore = 0f, uint? year = null, string? originalLanguage = null) { this.MangaId = TokenGen.CreateToken(typeof(Manga), mangaConnector.Name, idOnConnector); this.IdOnConnectorSite = idOnConnector; this.Name = name; this.Description = description; this.WebsiteUrl = websiteUrl; this.CoverUrl = coverUrl; this.ReleaseStatus = releaseStatus; this.LibraryId = library?.LocalLibraryId; this.Library = library; this.MangaConnectorName = mangaConnector.Name; this.MangaConnector = mangaConnector; this.Authors = authors; this.MangaTags = mangaTags; this.Links = links; this.AltTitles = altTitles; this.IgnoreChaptersBefore = ignoreChaptersBefore; this.DirectoryName = CleanDirectoryName(name); this.Year = year; this.OriginalLanguage = originalLanguage; } /// /// EF ONLY!!! /// public Manga(string mangaId, string idOnConnectorSite, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, string mangaConnectorName, string directoryName, float ignoreChaptersBefore, string? libraryId, uint? year, string? originalLanguage) { this.MangaId = mangaId; this.IdOnConnectorSite = idOnConnectorSite; this.Name = name; this.Description = description; this.WebsiteUrl = websiteUrl; this.CoverUrl = coverUrl; this.ReleaseStatus = releaseStatus; this.MangaConnectorName = mangaConnectorName; this.DirectoryName = directoryName; this.LibraryId = libraryId; this.IgnoreChaptersBefore = ignoreChaptersBefore; this.Year = year; this.OriginalLanguage = originalLanguage; } public string CreatePublicationFolder() { string? publicationFolder = FullDirectoryPath; if (publicationFolder is null) throw new DirectoryNotFoundException("Publication folder not found"); if(!Directory.Exists(publicationFolder)) Directory.CreateDirectory(publicationFolder); if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) File.SetUnixFileMode(publicationFolder, GroupRead | GroupWrite | GroupExecute | OtherRead | OtherWrite | OtherExecute | UserRead | UserWrite | UserExecute); return publicationFolder; } //https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file //less than 32 is control *forbidden* //34 is " *forbidden* //42 is * *forbidden* //47 is / *forbidden* //58 is : *forbidden* //60 is < *forbidden* //62 is > *forbidden* //63 is ? *forbidden* //92 is \ *forbidden* //124 is | *forbidden* //127 is delete *forbidden* //Below 127 all except ******* private static readonly int[] ForbiddenCharsBelow127 = [34, 42, 47, 58, 60, 62, 63, 92, 124, 127]; //Above 127 none except ******* private static readonly int[] IncludeCharsAbove127 = [128, 138, 142]; //128 is € include //138 is Š include //142 is Ž include //152 through 255 looks fine except 157, 172, 173, 175 ******* private static readonly int[] ForbiddenCharsAbove152 = [157, 172, 173, 175]; private static string CleanDirectoryName(string name) { StringBuilder sb = new (); foreach (char c in name) { if (c >= 32 && c < 127 && ForbiddenCharsBelow127.Contains(c) == false) sb.Append(c); else if (c > 127 && c < 152 && IncludeCharsAbove127.Contains(c)) sb.Append(c); else if(c >= 152 && c <= 255 && ForbiddenCharsAbove152.Contains(c) == false) sb.Append(c); } return sb.ToString(); } public override string ToString() { return $"{MangaId} {Name}"; } }