using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.InteropServices; using API.Workers; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using static System.IO.UnixFileMode; namespace API.Schema.MangaContext; [PrimaryKey("Key")] public class Manga : Identifiable { [StringLength(512)] public string Name { get; internal set; } [Required] public string Description { get; internal set; } [Url] [StringLength(512)] public string CoverUrl { get; internal set; } public MangaReleaseStatus ReleaseStatus { get; internal set; } [StringLength(64)] public string? LibraryId { get; private set; } public FileLibrary? Library = 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!; public float IgnoreChaptersBefore { get; internal set; } [StringLength(1024)] [Required] public string DirectoryName { get; private set; } [StringLength(512)] public string? CoverFileNameInCache { get; internal set; } public uint? Year { get; internal init; } [StringLength(8)] public string? OriginalLanguage { get; internal init; } [NotMapped] public string? FullDirectoryPath => Library is not null ? Path.Join(Library.BasePath, DirectoryName) : null; [NotMapped] public ICollection ChapterIds => Chapters.Select(c => c.Key).ToList(); public ICollection Chapters = null!; [NotMapped] public Dictionary IdsOnMangaConnectors => MangaConnectorIds.ToDictionary(id => id.MangaConnectorName, id => id.IdOnConnectorSite); [NotMapped] public ICollection MangaConnectorIdsIds => MangaConnectorIds.Select(id => id.Key).ToList(); public ICollection> MangaConnectorIds = null!; public Manga(string name, string description, string coverUrl, MangaReleaseStatus releaseStatus, ICollection authors, ICollection mangaTags, ICollection links, ICollection altTitles, FileLibrary? library = null, float ignoreChaptersBefore = 0f, uint? year = null, string? originalLanguage = null) :base(TokenGen.CreateToken(typeof(Manga), name)) { this.Name = name; this.Description = description; this.CoverUrl = coverUrl; this.ReleaseStatus = releaseStatus; this.Library = library; this.Authors = authors; this.MangaTags = mangaTags; this.Links = links; this.AltTitles = altTitles; this.IgnoreChaptersBefore = ignoreChaptersBefore; this.DirectoryName = name.CleanNameForWindows(); this.Year = year; this.OriginalLanguage = originalLanguage; this.Chapters = []; this.MangaConnectorIds = []; } /// /// EF ONLY!!! /// public Manga(string key, string name, string description, string coverUrl, MangaReleaseStatus releaseStatus, string directoryName, float ignoreChaptersBefore, string? libraryId, uint? year, string? originalLanguage) : base(key) { this.Name = name; this.Description = description; this.CoverUrl = coverUrl; this.ReleaseStatus = releaseStatus; 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)) if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) Directory.CreateDirectory(publicationFolder, UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute ); else Directory.CreateDirectory(publicationFolder); return publicationFolder; } /// /// Merges another Manga (MangaConnectorIds and Chapters) /// /// The other to merge /// to use for Database operations /// An array of for moving to new Directory public BaseWorker[] MergeFrom(Manga other, MangaContext context) { context.Mangas.Remove(other); List newJobs = new(); this.MangaConnectorIds = this.MangaConnectorIds .UnionBy(other.MangaConnectorIds, id => id.MangaConnectorName) .ToList(); foreach (Chapter otherChapter in other.Chapters) { string oldPath = otherChapter.FullArchiveFilePath; Chapter newChapter = new(this, otherChapter.ChapterNumber, otherChapter.VolumeNumber, otherChapter.Title); this.Chapters.Add(newChapter); string newPath = newChapter.FullArchiveFilePath; newJobs.Add(new MoveFileOrFolderWorker(newPath, oldPath)); } return newJobs.ToArray(); } public async Task<(MemoryStream stream, FileInfo fileInfo)?> GetCoverImage(string cachePath, CancellationToken ct) { string fullPath = Path.Join(cachePath, CoverFileNameInCache); if (!File.Exists(fullPath)) return null; FileInfo fileInfo = new(fullPath); MemoryStream stream = new (await File.ReadAllBytesAsync(fullPath, ct)); return (stream, fileInfo); } public override string ToString() => $"{base.ToString()} {Name}"; } [JsonConverter(typeof(StringEnumConverter))] public enum MangaReleaseStatus : byte { Continuing = 0, Completed = 1, OnHiatus = 2, Cancelled = 3, Unreleased = 4 }