diff --git a/Marechai.sln.DotSettings b/Marechai.sln.DotSettings index c57070a6..f5e885f9 100644 --- a/Marechai.sln.DotSettings +++ b/Marechai.sln.DotSettings @@ -1,5 +1,9 @@  + True True + True + True + True True True True diff --git a/Marechai/Areas/Admin/Controllers/MachinePhotosController.cs b/Marechai/Areas/Admin/Controllers/MachinePhotosController.cs deleted file mode 100644 index ce3b4cee..00000000 --- a/Marechai/Areas/Admin/Controllers/MachinePhotosController.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Marechai.Areas.Admin.Models; -using Marechai.Database.Models; -using Marechai.Helpers; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.EntityFrameworkCore; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats; - -namespace Marechai.Areas.Admin.Controllers -{ - [Area("Admin"), Authorize] - public class MachinePhotosController : Controller - { - readonly MarechaiContext _context; - readonly IWebHostEnvironment hostingEnvironment; - readonly UserManager userManager; - - public MachinePhotosController(MarechaiContext context, IWebHostEnvironment hostingEnvironment, - UserManager userManager) - { - _context = context; - this.hostingEnvironment = hostingEnvironment; - this.userManager = userManager; - } - - // GET: MachinePhotos - public async Task Index() => View(await _context. - MachinePhotos.Include(m => m.Machine). - Include(m => m.Machine.Company).Include(m => m.User). - Select(p => new MachinePhotoViewModel - { - Id = p.Id, Author = p.Author, - License = p.License.Name, - Machine = - $"{p.Machine.Company.Name} {p.Machine.Name}", - UploadDate = p.UploadDate, - UploadUser = p.User.UserName, - LicenseId = p.License.Id - }).OrderBy(p => p.Machine).ThenBy(p => p.UploadUser). - ThenBy(p => p.UploadDate).ToListAsync()); - - // GET: MachinePhotos/Details/5 - public async Task Details(Guid? id) - { - if(id == null) - return NotFound(); - - MachinePhotoDetailsViewModel machinePhoto = - await _context.MachinePhotos.Select(m => new MachinePhotoDetailsViewModel - { - Id = m.Id, CameraManufacturer = m.CameraManufacturer, CameraModel = m.CameraModel, - ColorSpace = m.ColorSpace, Comments = m.Comments, Contrast = m.Contrast, - CreationDate = m.CreationDate, DigitalZoomRatio = m.DigitalZoomRatio, ExifVersion = m.ExifVersion, - Exposure = m.ExposureTime, ExposureProgram = m.ExposureProgram, Flash = m.Flash, Focal = m.Focal, - FocalLength = m.FocalLength, FocalLengthEquivalent = m.FocalLengthEquivalent, - HorizontalResolution = m.HorizontalResolution, IsoRating = m.IsoRating, Lens = m.Lens, - LightSource = m.LightSource, MeteringMode = m.MeteringMode, ResolutionUnit = m.ResolutionUnit, - Orientation = m.Orientation, Saturation = m.Saturation, SceneCaptureType = m.SceneCaptureType, - SensingMethod = m.SensingMethod, Sharpness = m.Sharpness, SoftwareUsed = m.SoftwareUsed, - SubjectDistanceRange = m.SubjectDistanceRange, UploadDate = m.UploadDate, - VerticalResolution = m.VerticalResolution, WhiteBalance = m.WhiteBalance, License = m.License.Name, - UploadUser = m.User.UserName, Machine = $"{m.Machine.Company.Name} {m.Machine.Name}", - MachineId = m.Machine.Id, Source = m.Source - }).FirstOrDefaultAsync(m => m.Id == id); - - if(machinePhoto == null) - return NotFound(); - - return View(machinePhoto); - } - - // GET: MachinePhotos/Create - public IActionResult Create() - { - ViewData["MachineId"] = new SelectList(_context.Machines.OrderBy(c => c.Company.Name).ThenBy(c => c.Name). - Select(m => new - { - m.Id, Name = $"{m.Company.Name} {m.Name}" - }), "Id", "Name"); - - ViewData["LicenseId"] = new SelectList(_context.Licenses.OrderBy(c => c.Name).Select(l => new - { - l.Id, l.Name - }), "Id", "Name"); - - return View(); - } - - // POST: MachinePhotos/Create - // To protect from overposting attacks, please enable the specific properties you want to bind to, for - // more details see http://go.microsoft.com/fwlink/?LinkId=317598. - [HttpPost, ValidateAntiForgeryToken] - public async Task Create([Bind("MachineId,LicenseId,Photo,Source")] - MachinePhotoViewModel machinePhoto) - { - if(!ModelState.IsValid) - return View(machinePhoto); - - var newId = Guid.NewGuid(); - string tmpPath = Path.GetTempPath(); - string tmpFileName = newId + ".tmp"; - string tmpFile = Path.Combine(tmpPath, tmpFileName); - - if(System.IO.File.Exists(tmpFile)) - { - machinePhoto.ErrorMessage = "Colliding temp file please retry."; - - return View(machinePhoto); - } - - using(var tmpStream = new FileStream(tmpFile, FileMode.CreateNew)) - await machinePhoto.Photo.CopyToAsync(tmpStream); - - IImageFormat imageinfo = Image.DetectFormat(tmpFile); - - string extension; - - switch(imageinfo?.Name) - { - case"JPEG": - extension = ".jpg"; - - break; - case"PNG": - extension = ".png"; - - break; - default: - System.IO.File.Delete(tmpFile); - machinePhoto.ErrorMessage = "Unsupported file format, only JPEG and PNG are allowed at the moment."; - - return View(machinePhoto); - } - - MachinePhoto photo = Photos.Add(tmpFile, newId, hostingEnvironment.WebRootPath, - hostingEnvironment.ContentRootPath, extension); - - photo.Id = newId; - photo.User = await userManager.GetUserAsync(HttpContext.User); - photo.License = await _context.Licenses.FindAsync(machinePhoto.LicenseId); - photo.Machine = await _context.Machines.FindAsync(machinePhoto.MachineId); - - _context.Add(photo); - await _context.SaveChangesAsync(); - - return RedirectToAction(nameof(Details), new - { - Id = newId - }); - } - - // GET: MachinePhotos/Edit/5 - public async Task Edit(Guid? id) - { - if(id == null) - return NotFound(); - - MachinePhoto machinePhoto = await _context.MachinePhotos.FindAsync(id); - - if(machinePhoto == null) - return NotFound(); - - ViewData["MachineId"] = new SelectList(_context.Machines.OrderBy(m => m.Company.Name).ThenBy(m => m.Name). - Select(m => new - { - m.Id, Name = $"{m.Company.Name} {m.Name}" - }), "Id", "Name", machinePhoto.MachineId); - - ViewData["LicenseId"] = new SelectList(_context.Licenses.OrderBy(l => l.Name).Select(l => new - { - l.Id, l.Name - }), "Id", "Name", machinePhoto.LicenseId); - - return View(machinePhoto); - } - - // POST: MachinePhotos/Edit/5 - // To protect from overposting attacks, please enable the specific properties you want to bind to, for - // more details see http://go.microsoft.com/fwlink/?LinkId=317598. - [HttpPost, ValidateAntiForgeryToken] - public async Task Edit( - Guid id, - [Bind("Author,CameraManufacturer,CameraModel,ColorSpace,Comments,Contrast,CreationDate,DigitalZoomRatio,ExifVersion,Exposure,ExposureMethod,ExposureProgram,Flash,Focal,FocalLength,FocalLengthEquivalent,HorizontalResolution,IsoRating,Lens,LicenseId,LightSource,MachineId,MeteringMode,ResolutionUnit,Orientation,Saturation,SceneCaptureType,SensingMethod,Sharpness,SoftwareUsed,SubjectDistanceRange,VerticalResolution,WhiteBalance,Id,Source")] - MachinePhoto machinePhoto) - { - if(id != machinePhoto.Id) - return NotFound(); - - if(!ModelState.IsValid) - return View(machinePhoto); - - try - { - _context.Update(machinePhoto); - await _context.SaveChangesAsync(); - } - catch(DbUpdateConcurrencyException) - { - if(!MachinePhotoExists(machinePhoto.Id)) - return NotFound(); - - throw; - } - - return RedirectToAction(nameof(Index)); - } - - // GET: MachinePhotos/Delete/5 - public async Task Delete(Guid? id) - { - if(id == null) - return NotFound(); - - MachinePhotoViewModel machinePhoto = - await _context.MachinePhotos.Include(m => m.Machine).Include(m => m.Machine.Company). - Include(m => m.User).Select(p => new MachinePhotoViewModel - { - Id = p.Id, Author = p.Author, License = p.License.Name, - Machine = $"{p.Machine.Company.Name} {p.Machine.Name}", UploadDate = p.UploadDate, - UploadUser = p.User.UserName, LicenseId = p.License.Id - }).OrderBy(p => p.Machine).ThenBy(p => p.UploadUser).ThenBy(p => p.UploadDate). - FirstOrDefaultAsync(m => m.Id == id); - - if(machinePhoto == null) - return NotFound(); - - return View(machinePhoto); - } - - // POST: MachinePhotos/Delete/5 - [HttpPost, ActionName("Delete"), ValidateAntiForgeryToken] - public async Task DeleteConfirmed(Guid id) - { - MachinePhoto machinePhoto = await _context.MachinePhotos.FindAsync(id); - _context.MachinePhotos.Remove(machinePhoto); - await _context.SaveChangesAsync(); - - foreach(string format in new[] - { - "jpg", "webp" - }) - { - foreach(int multiplier in new[] - { - 1, 2, 3 - }) - System.IO.File.Delete(Path.Combine(hostingEnvironment.WebRootPath, "assets/photos/machines/thumbs", - format, $"{multiplier}x", id + $".{format}")); - } - - return RedirectToAction(nameof(Index)); - } - - bool MachinePhotoExists(Guid id) => _context.MachinePhotos.Any(e => e.Id == id); - } -} \ No newline at end of file diff --git a/Marechai/Areas/Admin/Views/MachinePhotos/Create.cshtml b/Marechai/Areas/Admin/Views/MachinePhotos/Create.cshtml deleted file mode 100644 index f74272fc..00000000 --- a/Marechai/Areas/Admin/Views/MachinePhotos/Create.cshtml +++ /dev/null @@ -1,87 +0,0 @@ -@using Marechai.Database.Models -@using Microsoft.AspNetCore.Identity -@model Marechai.Areas.Admin.Models.MachinePhotoViewModel -@inject UserManager UserManager - -@{ - ViewData["Title"] = "Create"; -} -

Create

-

Machine photo

-
-
-
-

- By uploading this photo you confirm you have the rights on it, and you authorize us the non-exclusive rights of show, and distribution, of the photo in database, and the associated mobile application, under the license chosen below. -

-

- You also confirm that this photo corresponds, to the best of your knowledge, to the specified machine, accompanying materials (such as box, cabling or manuals on the time of retail). -

-

- Photos that are not clear enough or of too small resolution will be removed of the database. -

-

- @* TODO Detect roles *@ - This photo will not appear publicly until approved to an administrator. -

-

- Upload of inappropriate or unrelated photos will create a warning in your account. Several warnings mean your account would be terminated. -

-

- Removal of your account do not automatically revoke our license to use your photos. If you want to remove your photos contact with an administrator before you remove your account. Failure to do so makes us technically unable to remove your photos in the future. -

-
-
-
-
-
-
-
-
- - -
-
- - -
-
- - - - -
-
- - -
-
- - - - - @if (Model?.ErrorMessage != null) - { - @Model.ErrorMessage - } -
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} \ No newline at end of file diff --git a/Marechai/Areas/Admin/Views/MachinePhotos/Delete.cshtml b/Marechai/Areas/Admin/Views/MachinePhotos/Delete.cshtml deleted file mode 100644 index 9c9a4c85..00000000 --- a/Marechai/Areas/Admin/Views/MachinePhotos/Delete.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@model Marechai.Areas.Admin.Models.MachinePhotoViewModel - -@{ - ViewData["Title"] = "Delete"; -} -

Delete

-

Are you sure you want to delete this?

-
-

Machine photo

-
-
-
- @Html.DisplayNameFor(model => model.Author) -
-
- @Html.DisplayFor(model => model.Author) -
-
- @Html.DisplayNameFor(model => model.License) -
-
- @Html.DisplayFor(model => model.License) -
-
- @Html.DisplayNameFor(model => model.Machine) -
-
- @Html.DisplayFor(model => model.Machine) -
-
- @Html.DisplayNameFor(model => model.UploadDate) -
-
- @Html.DisplayFor(model => model.UploadDate) -
-
- @Html.DisplayNameFor(model => model.UploadUser) -
-
- @Html.DisplayFor(model => model.UploadUser) -
-
- -
-
-
- - - - Back to List - -
-
\ No newline at end of file diff --git a/Marechai/Areas/Admin/Views/MachinePhotos/Details.cshtml b/Marechai/Areas/Admin/Views/MachinePhotos/Details.cshtml deleted file mode 100644 index f99bd026..00000000 --- a/Marechai/Areas/Admin/Views/MachinePhotos/Details.cshtml +++ /dev/null @@ -1,340 +0,0 @@ -@model Marechai.Areas.Admin.Models.MachinePhotoDetailsViewModel - -@{ - ViewData["Title"] = "Details"; -} -

Details

-
-

Machine photo

-
-
-
- @Html.DisplayNameFor(model => model.UploadUser) -
-
- @Html.DisplayFor(model => model.UploadUser) -
-
- @Html.DisplayNameFor(model => model.UploadDate) -
-
- @Html.DisplayFor(model => model.UploadDate) -
-
- @Html.DisplayNameFor(model => model.License) -
-
- @Html.DisplayFor(model => model.License) -
-
- @Html.DisplayNameFor(model => model.Machine) -
-
- - @Html.DisplayFor(model => model.Machine) - -
-
- @Html.DisplayNameFor(model => model.Source) -
-
- @if (Model.Source != null) - { - Link - } -
-
- -
-
- -
- -@if (Model.Author != null) -{ -
- @Html.DisplayNameFor(model => model.Author) -
-
- @Html.DisplayFor(model => model.Author) -
-} -@if (Model.CameraManufacturer != null) -{ -
- @Html.DisplayNameFor(model => model.CameraManufacturer) -
-
- @Html.DisplayFor(model => model.CameraManufacturer) -
-} -@if (Model.CameraModel != null) -{ -
- @Html.DisplayNameFor(model => model.CameraModel) -
-
- @Html.DisplayFor(model => model.CameraModel) -
-} -@if (Model.ColorSpace != null) -{ -
- @Html.DisplayNameFor(model => model.ColorSpace) -
-
- @Html.DisplayFor(model => model.ColorSpace) -
-} -@if (Model.Comments != null) -{ -
- @Html.DisplayNameFor(model => model.Comments) -
-
- @Html.DisplayFor(model => model.Comments) -
-} -@if (Model.Contrast != null) -{ -
- @Html.DisplayNameFor(model => model.Contrast) -
-
- @Html.DisplayFor(model => model.Contrast) -
-} -@if (Model.CreationDate != null) -{ -
- @Html.DisplayNameFor(model => model.CreationDate) -
-
- @Html.DisplayFor(model => model.CreationDate) -
-} -@if (Model.DigitalZoomRatio != null) -{ -
- @Html.DisplayNameFor(model => model.DigitalZoomRatio) -
-
- @Html.DisplayFor(model => model.DigitalZoomRatio) -
-} -@if (Model.ExifVersion != null) -{ -
- @Html.DisplayNameFor(model => model.ExifVersion) -
-
- @Html.DisplayFor(model => model.ExifVersion) -
-} -@if (Model.Exposure != null) -{ -
- @Html.DisplayNameFor(model => model.Exposure) -
-
- @Html.DisplayFor(model => model.Exposure) -
-} -@if (Model.ExposureMethod != null) -{ -
- @Html.DisplayNameFor(model => model.ExposureMethod) -
-
- @Html.DisplayFor(model => model.ExposureMethod) -
-} -@if (Model.ExposureProgram != null) -{ -
- @Html.DisplayNameFor(model => model.ExposureProgram) -
-
- @Html.DisplayFor(model => model.ExposureProgram) -
-} -@if (Model.Flash != null) -{ -
- @Html.DisplayNameFor(model => model.Flash) -
-
- @Html.DisplayFor(model => model.Flash) -
-} -@if (Model.Focal != null) -{ -
- @Html.DisplayNameFor(model => model.Focal) -
-
- @Html.DisplayFor(model => model.Focal) -
-} -@if (Model.FocalLength != null) -{ -
- @Html.DisplayNameFor(model => model.FocalLength) -
-
- @Html.DisplayFor(model => model.FocalLength) -
-} -@if (Model.FocalLengthEquivalent != null) -{ -
- @Html.DisplayNameFor(model => model.FocalLengthEquivalent) -
-
- @Html.DisplayFor(model => model.FocalLengthEquivalent) -
-} -@if (Model.HorizontalResolution != null) -{ -
- @Html.DisplayNameFor(model => model.HorizontalResolution) -
-
- @Html.DisplayFor(model => model.HorizontalResolution) -
-} -@if (Model.IsoRating != null) -{ -
- @Html.DisplayNameFor(model => model.IsoRating) -
-
- @Html.DisplayFor(model => model.IsoRating) -
-} -@if (Model.Lens != null) -{ -
- @Html.DisplayNameFor(model => model.Lens) -
-
- @Html.DisplayFor(model => model.Lens) -
-} -@if (Model.LightSource != null) -{ -
- @Html.DisplayNameFor(model => model.LightSource) -
-
- @Html.DisplayFor(model => model.LightSource) -
-} -@if (Model.MeteringMode != null) -{ -
- @Html.DisplayNameFor(model => model.MeteringMode) -
-
- @Html.DisplayFor(model => model.MeteringMode) -
-} -@if (Model.ResolutionUnit != null) -{ -
- @Html.DisplayNameFor(model => model.ResolutionUnit) -
-
- @Html.DisplayFor(model => model.ResolutionUnit) -
-} -@if (Model.Orientation != null) -{ -
- @Html.DisplayNameFor(model => model.Orientation) -
-
- @Html.DisplayFor(model => model.Orientation) -
-} -@if (Model.Saturation != null) -{ -
- @Html.DisplayNameFor(model => model.Saturation) -
-
- @Html.DisplayFor(model => model.Saturation) -
-} -@if (Model.SceneCaptureType != null) -{ -
- @Html.DisplayNameFor(model => model.SceneCaptureType) -
-
- @Html.DisplayFor(model => model.SceneCaptureType) -
-} -@if (Model.SensingMethod != null) -{ -
- @Html.DisplayNameFor(model => model.SensingMethod) -
-
- @Html.DisplayFor(model => model.SensingMethod) -
-} -@if (Model.Sharpness != null) -{ -
- @Html.DisplayNameFor(model => model.Sharpness) -
-
- @Html.DisplayFor(model => model.Sharpness) -
-} -@if (Model.SoftwareUsed != null) -{ -
- @Html.DisplayNameFor(model => model.SoftwareUsed) -
-
- @Html.DisplayFor(model => model.SoftwareUsed) -
-} -@if (Model.SubjectDistanceRange != null) -{ -
- @Html.DisplayNameFor(model => model.SubjectDistanceRange) -
-
- @Html.DisplayFor(model => model.SubjectDistanceRange) -
-} -@if (Model.VerticalResolution != null) -{ -
- @Html.DisplayNameFor(model => model.VerticalResolution) -
-
- @Html.DisplayFor(model => model.VerticalResolution) -
-} -@if (Model.WhiteBalance != null) -{ -
- @Html.DisplayNameFor(model => model.WhiteBalance) -
-
- @Html.DisplayFor(model => model.WhiteBalance) -
-} -
-
\ No newline at end of file diff --git a/Marechai/Areas/Admin/Views/MachinePhotos/Edit.cshtml b/Marechai/Areas/Admin/Views/MachinePhotos/Edit.cshtml deleted file mode 100644 index 9e3f7740..00000000 --- a/Marechai/Areas/Admin/Views/MachinePhotos/Edit.cshtml +++ /dev/null @@ -1,296 +0,0 @@ -@using Marechai.Database -@model Marechai.Database.Models.MachinePhoto - -@{ - ViewData["Title"] = "Edit"; -} -

Edit

-

Machine photo

-
-
-
-
-
-
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
- - -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} \ No newline at end of file diff --git a/Marechai/Areas/Admin/Views/MachinePhotos/Index.cshtml b/Marechai/Areas/Admin/Views/MachinePhotos/Index.cshtml deleted file mode 100644 index 2c8621b0..00000000 --- a/Marechai/Areas/Admin/Views/MachinePhotos/Index.cshtml +++ /dev/null @@ -1,77 +0,0 @@ -@model IEnumerable - -@{ - ViewData["Title"] = "Index"; -} -

Machine photos

-

- - Create new - -

- - - - - - - - - - - - - - @foreach (var item in Model) - { - - - - - - - - - - } - -
- @Html.DisplayNameFor(model => model.Machine) - - @Html.DisplayNameFor(model => model.UploadUser) - - @Html.DisplayNameFor(model => model.UploadDate) - - @Html.DisplayNameFor(model => model.Author) - - @Html.DisplayNameFor(model => model.License) -
- - - - - - @Html.DisplayFor(modelItem => item.Machine) - - @Html.DisplayFor(modelItem => item.UploadUser) - - @Html.DisplayFor(modelItem => item.UploadDate) - - @Html.DisplayFor(modelItem => item.Author) - - @Html.DisplayFor(modelItem => item.License) - - - Details - - - Edit - - - Delete - -
\ No newline at end of file diff --git a/Marechai/Helpers/AddPhoto.cs b/Marechai/Helpers/AddPhoto.cs deleted file mode 100644 index a83a2ce8..00000000 --- a/Marechai/Helpers/AddPhoto.cs +++ /dev/null @@ -1,406 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using Marechai.Database; -using Marechai.Database.Models; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SkiaSharp; - -namespace Marechai.Helpers -{ - public class Photos - { - public static MachinePhoto Add(string tmpFile, Guid newId, string webRootPath, string contentRootPath, - string extension) - { - Image image = Image.Load(tmpFile); - - var photo = new MachinePhoto(); - - foreach(ImageProperty prop in image.MetaData.Properties) - switch(prop.Name) - { - case"aux:Lens": - photo.Lens = prop.Value; - - break; - } - - if(image.MetaData.ExifProfile != null) - foreach(ExifValue exif in image.MetaData.ExifProfile.Values.ToList()) - switch(exif.Tag) - { - case ExifTag.Artist: - photo.Author = exif.Value as string; - - break; - case ExifTag.Make: - photo.CameraManufacturer = exif.Value as string; - - break; - case ExifTag.ColorSpace: - photo.ColorSpace = (ColorSpace)exif.Value; - - break; - case ExifTag.UserComment: - photo.Comments = Encoding.ASCII.GetString(exif.Value as byte[]); - - break; - case ExifTag.Contrast: - photo.Contrast = (Contrast)exif.Value; - - break; - case ExifTag.DateTimeDigitized: - photo.CreationDate = - DateTime.ParseExact(exif.Value.ToString(), "yyyy:MM:dd HH:mm:ss", - CultureInfo.InvariantCulture); - - break; - case ExifTag.DigitalZoomRatio: - photo.DigitalZoomRatio = ((Rational)exif.Value).ToDouble(); - - break; - case ExifTag.ExifVersion: - photo.ExifVersion = Encoding.ASCII.GetString(exif.Value as byte[]); - - break; - case ExifTag.ExposureTime: - { - var rat = (Rational)exif.Value; - photo.Exposure = rat.Denominator == 1 ? rat.Numerator.ToString() : rat.ToString(); - - break; - } - - case ExifTag.ExposureMode: - photo.ExposureMethod = (ExposureMode)exif.Value; - - break; - case ExifTag.ExposureProgram: - photo.ExposureProgram = (ExposureProgram)exif.Value; - - break; - case ExifTag.Flash: - photo.Flash = (Flash)exif.Value; - - break; - case ExifTag.FNumber: - photo.Focal = ((Rational)exif.Value).ToDouble(); - - break; - case ExifTag.FocalLength: - photo.FocalLength = ((Rational)exif.Value).ToDouble(); - - break; - case ExifTag.FocalLengthIn35mmFilm: - photo.FocalLengthEquivalent = exif.Value as ushort?; - - break; - case ExifTag.XResolution: - photo.HorizontalResolution = ((Rational)exif.Value).ToDouble(); - - break; - case ExifTag.ISOSpeedRatings: - photo.IsoRating = (ushort)exif.Value; - - break; - case ExifTag.LensModel: - photo.Lens = exif.Value as string; - - break; - case ExifTag.LightSource: - photo.LightSource = (LightSource)exif.Value; - - break; - case ExifTag.MeteringMode: - photo.MeteringMode = (MeteringMode)exif.Value; - - break; - case ExifTag.ResolutionUnit: - photo.ResolutionUnit = (ResolutionUnit)exif.Value; - - break; - case ExifTag.Orientation: - photo.Orientation = (Orientation)exif.Value; - - break; - case ExifTag.Saturation: - photo.Saturation = (Saturation)exif.Value; - - break; - case ExifTag.SceneCaptureType: - photo.SceneCaptureType = (SceneCaptureType)exif.Value; - - break; - case ExifTag.SensingMethod: - photo.SensingMethod = (SensingMethod)exif.Value; - - break; - case ExifTag.Software: - photo.SoftwareUsed = exif.Value as string; - - break; - case ExifTag.SubjectDistanceRange: - photo.SubjectDistanceRange = (SubjectDistanceRange)exif.Value; - - break; - case ExifTag.YResolution: - photo.VerticalResolution = ((Rational)exif.Value).ToDouble(); - - break; - case ExifTag.WhiteBalance: - photo.WhiteBalance = (WhiteBalance)exif.Value; - - break; - default: - image.MetaData.ExifProfile.RemoveValue(exif.Tag); - - break; - } - - if(!Directory.Exists(Path.Combine(webRootPath, "assets", "photos"))) - Directory.CreateDirectory(Path.Combine(webRootPath, "assets", "photos")); - - if(!Directory.Exists(Path.Combine(webRootPath, "assets", "photos", "machines"))) - Directory.CreateDirectory(Path.Combine(webRootPath, "assets", "photos", "machines")); - - if(!Directory.Exists(Path.Combine(webRootPath, "assets", "photos", "machines", "thumbs"))) - Directory.CreateDirectory(Path.Combine(webRootPath, "assets", "photos", "machines", "thumbs")); - - string outJpeg = Path.Combine(webRootPath, "assets", "photos", "machines", newId + ".jpg"); - - using(var jpegStream = new FileStream(outJpeg, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None)) - image.SaveAsJpeg(jpegStream); - - int imgMax = Math.Max(image.Width, image.Height); - int width = image.Width; - int height = image.Height; - - image.Dispose(); - - var skBitmap = SKBitmap.Decode(outJpeg); - - foreach(string format in new[] - { - "jpg", "webp" - }) - { - if(!Directory.Exists(Path.Combine(webRootPath, "assets/photos/machines/thumbs", format))) - Directory.CreateDirectory(Path.Combine(webRootPath, "assets/photos/machines/thumbs", format)); - - SKEncodedImageFormat skFormat; - - switch(format) - { - case"webp": - skFormat = SKEncodedImageFormat.Webp; - - break; - default: - skFormat = SKEncodedImageFormat.Jpeg; - - break; - } - - foreach(int multiplier in new[] - { - 1, 2, 3 - }) - { - if(!Directory.Exists(Path.Combine(webRootPath, "assets/photos/machines/thumbs", format, - $"{multiplier}x"))) - Directory.CreateDirectory(Path.Combine(webRootPath, "assets/photos/machines/thumbs", format, - $"{multiplier}x")); - - string resized = Path.Combine(webRootPath, "assets/photos/machines/thumbs", format, - $"{multiplier}x", newId + $".{format}"); - - if(File.Exists(resized)) - continue; - - float canvasMin = 256 * multiplier; - - float scale = canvasMin / imgMax; - - // Do not enlarge images - if(scale > 1) - scale = 1; - - SKBitmap skResized = skBitmap.Resize(new SKImageInfo((int)(width * scale), (int)(height * scale)), - SKFilterQuality.High); - - var skImage = SKImage.FromBitmap(skResized); - SKData data = skImage.Encode(skFormat, 100); - var outfs = new FileStream(resized, FileMode.CreateNew); - data.SaveTo(outfs); - outfs.Close(); - } - } - - if(!Directory.Exists(Path.Combine(contentRootPath, "originals", "photos"))) - Directory.CreateDirectory(Path.Combine(contentRootPath, "originals", "photos")); - - if(!Directory.Exists(Path.Combine(contentRootPath, "originals", "photos"))) - Directory.CreateDirectory(Path.Combine(contentRootPath, "originals", "photos")); - - if(!Directory.Exists(Path.Combine(contentRootPath, "originals", "photos", "machines"))) - Directory.CreateDirectory(Path.Combine(contentRootPath, "originals", "photos", "machines")); - - File.Move(tmpFile, Path.Combine(contentRootPath, "originals", "photos", "machines", newId + extension)); - - return photo; - } - - public static void ImportPhotos(MarechaiContext context) - { - if(!Directory.Exists("wwwroot/assets/photos/computers") && - !Directory.Exists("wwwroot/assets/photos/consoles")) - return; - - if(!(context.Users.FirstOrDefault() is ApplicationUser user)) - { - Console.WriteLine("Cannot import photos without an existing uberadmin, please create it before continuing..."); - - return; - } - - License license = context.Licenses.FirstOrDefault(l => l.Name == "Fair use"); - - if(license is null) - { - Console.WriteLine("Cannot import photos without the \"Fair use\" license, please create it before continuing..."); - - return; - } - - foreach(string computer in Directory.EnumerateDirectories("wwwroot/assets/photos/computers", "*", - SearchOption.TopDirectoryOnly)) - { - string computerIdStr = Path.GetFileName(computer); - - if(!int.TryParse(computerIdStr, out int computerId)) - { - Console.WriteLine("{0} does not indicate a correct computer ID", computerIdStr); - - continue; - } - - Machine machine = context.Machines.FirstOrDefault(m => m.Id == computerId); - - if(machine is null) - { - Console.WriteLine("Cannot find machine with ID {0}.", computerId); - - continue; - } - - foreach(string computerPhoto in Directory.EnumerateFiles(computer, "*", SearchOption.TopDirectoryOnly)) - { - Console.WriteLine("Importing {0}...", computerPhoto); - var newId = Guid.NewGuid(); - - IImageFormat imageinfo = Image.DetectFormat(computerPhoto); - - string extension; - - switch(imageinfo?.Name) - { - case"JPEG": - extension = ".jpg"; - - break; - case"PNG": - extension = ".png"; - - break; - case"GIF": - extension = ".gif"; - - break; - default: - Console.WriteLine("Unsupported file format for {0}", computerPhoto); - - continue; - } - - MachinePhoto photo = Add(computerPhoto, newId, "wwwroot", ".", extension); - - photo.Id = newId; - photo.User = user; - photo.License = license; - photo.Machine = machine; - - context.Add(photo); - } - } - - foreach(string console in Directory.EnumerateDirectories("wwwroot/assets/photos/consoles", "*", - SearchOption.TopDirectoryOnly)) - { - string consoleIdStr = Path.GetFileName(console); - - if(!int.TryParse(consoleIdStr, out int consoleId)) - { - Console.WriteLine("{0} does not indicate a correct console ID", consoleIdStr); - - continue; - } - - Machine machine = context.Machines.FirstOrDefault(m => m.Id == consoleId + 356); - - if(machine is null) - { - Console.WriteLine("Cannot find machine with ID {0}.", consoleId + 356); - - continue; - } - - foreach(string consolePhoto in Directory.EnumerateFiles(console, "*", SearchOption.TopDirectoryOnly)) - { - Console.WriteLine("Importing {0}...", consolePhoto); - var newId = Guid.NewGuid(); - - IImageFormat imageinfo = Image.DetectFormat(consolePhoto); - - string extension; - - switch(imageinfo?.Name) - { - case"JPEG": - extension = ".jpg"; - - break; - case"PNG": - extension = ".png"; - - break; - case"GIF": - extension = ".gif"; - - break; - default: - Console.WriteLine("Unsupported file format for {0}", consolePhoto); - - continue; - } - - MachinePhoto photo = Add(consolePhoto, newId, "wwwroot", ".", extension); - - photo.Id = newId; - photo.User = user; - photo.License = license; - photo.Machine = machine; - - context.Add(photo); - } - } - } - } -} \ No newline at end of file diff --git a/Marechai/Helpers/Exif.cs b/Marechai/Helpers/Exif.cs new file mode 100644 index 00000000..69577693 --- /dev/null +++ b/Marechai/Helpers/Exif.cs @@ -0,0 +1,111 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using Marechai.Database; +using Marechai.ViewModels; + +namespace Marechai.Helpers +{ + public class Exif + { + public string Make { get; set; } + public string Model { get; set; } + public string Author { get; set; } + public ColorSpace? ColorSpace { get; set; } + public string Description { get; set; } + public Contrast Contrast { get; set; } + [JsonConverter(typeof(ExiftoolDateConverter))] + public DateTime? CreateDate { get; set; } + [JsonConverter(typeof(ExiftoolDateConverter))] + public DateTime? DateTimeOriginal { get; set; } + [JsonConverter(typeof(ExiftoolDateConverter))] + public DateTime? ModifyDate { get; set; } + public double? DigitalZoomRatio { get; set; } + public string ExifVersion { get; set; } + public double? ExposureTime { get; set; } + public ExposureMode? ExposureMode { get; set; } + public ExposureProgram? ExposureProgram { get; set; } + public Flash? Flash { get; set; } + public double? FNumber { get; set; } + public double? FocalLength { get; set; } + public double? FocalLengthIn35mmFormat { get; set; } + public double? XResolution { get; set; } + public ushort? ISO { get; set; } + public string LensModel { get; set; } + public string Lens { get; set; } + public LightSource? LightSource { get; set; } + public MeteringMode? MeteringMode { get; set; } + public ResolutionUnit? ResolutionUnit { get; set; } + public Orientation? Orientation { get; set; } + public Saturation? Saturation { get; set; } + public SceneCaptureType? SceneCaptureType { get; set; } + public SensingMethod? SensingMethod { get; set; } + public Sharpness? Sharpness { get; set; } + public string Software { get; set; } + public SubjectDistanceRange? SubjectDistanceRange { get; set; } + public double? YResolution { get; set; } + public WhiteBalance? WhiteBalance { get; set; } + public double? ApertureValue { get; set; } + + public void ToViewModel(BasePhotoViewModel model) + { + model.CameraManufacturer = Make; + model.CameraModel = Model; + model.Author = Author; + model.CreationDate = DateTimeOriginal ?? CreateDate ?? ModifyDate; + model.DigitalZoomRatio = DigitalZoomRatio; + model.ExifVersion = ExifVersion; + model.ExposureTime = ExposureTime; + model.Focal = FNumber; + model.HorizontalResolution = XResolution; + model.IsoRating = ISO; + model.Lens = LensModel ?? Lens; + model.SoftwareUsed = Software; + model.VerticalResolution = YResolution; + model.Aperture = ApertureValue; + model.ColorSpace = ColorSpace; + model.Contrast = Contrast; + model.ExposureMethod = ExposureMode; + model.ExposureProgram = ExposureProgram; + model.Flash = Flash; + model.FocalLength = FocalLength; + model.FocalLengthEquivalent = FocalLengthIn35mmFormat; + model.LightSource = LightSource; + model.MeteringMode = MeteringMode; + model.ResolutionUnit = ResolutionUnit; + model.Orientation = Orientation; + model.Saturation = Saturation; + model.SceneCaptureType = SceneCaptureType; + model.SensingMethod = SensingMethod; + model.Sharpness = Sharpness; + model.SubjectDistanceRange = SubjectDistanceRange; + model.WhiteBalance = WhiteBalance; + model.Comments = Description; + } + } + + internal class ExiftoolDateConverter : JsonConverter + { + public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + Debug.Assert(typeToConvert == typeof(DateTime?)); + string read = reader.GetString(); + + if(read is null) + return null; + + return DateTime.ParseExact(reader.GetString(), "yyyy':'MM':'dd' 'HH':'mm':'ssK", + CultureInfo.InvariantCulture); + } + + public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options) + { + if(value is null) + return; + + writer.WriteStringValue(value?.ToUniversalTime().ToString("yyyy':'MM':'dd' 'HH':'mm':'ssK")); + } + } +} \ No newline at end of file diff --git a/Marechai/Helpers/ImageMagick.cs b/Marechai/Helpers/ImageMagick.cs new file mode 100644 index 00000000..27a40aad --- /dev/null +++ b/Marechai/Helpers/ImageMagick.cs @@ -0,0 +1,137 @@ +namespace Marechai.Helpers +{ + public static class ImageMagick + { + public static string GetExtension(string format) + { + switch(format) + { + case"3FR": return".3fr"; + case"AAI": return".aai"; + case"AI": return".ai"; + case"ARW": return".arw"; + case"AVS": return".avs"; + case"BIE": return".jbg"; + case"BMP": return".bmp"; + case"BMP2": return".bmp"; + case"BMP3": return".bmp"; + case"CR2": return".cr2"; + case"CR3": return".cr3"; + case"CRW": return".crw"; + case"CUR": return".cur"; + case"CUT": return".cut"; + case"DCM": return".dcm"; + case"DCR": return".dcr"; + case"DCRAW": return".dng"; + case"DCX": return".dcx"; + case"DDS": return".dds"; + case"DNG": return".dng"; + case"DPX": return".dpx"; + case"DXT1": return".dds"; + case"DXT5": return".dds"; + case"EPDF": return".pdf"; + case"EPI": return".epi"; + case"EPS": return".eps"; + case"EPS2": return".eps"; + case"EPS3": return".eps"; + case"EPSF": return".eps"; + case"EPSI": return".epi"; + case"ERF": return".erf"; + case"EXR": return".exr"; + case"GIF": return".gif"; + case"GIF87": return".gif"; + case"GROUP4": return".tif"; + case"HDR": return".hdr"; + case"HEIC": return".heic"; + case"ICB": return".tga"; + case"ICO": return".ico"; + case"ICON": return".ico"; + case"J2C": return".jp2"; + case"J2K": return".jp2"; + case"JBG": return".jbg"; + case"JBIG": return".jbg"; + case"JNG": return".jng"; + case"JP2": return".jp2"; + case"JPC": return".jp2"; + case"JPE": return".jpg"; + case"JPEG": return".jpg"; + case"JPG": return".jpg"; + case"K25": return".k25"; + case"KDC": return".kdc"; + case"MIFF": return".miff"; + case"MNG": return".mng"; + case"MRW": return".mrw"; + case"NEF": return".nef"; + case"NRW": return".nrw"; + case"ORF": return".orf"; + case"PALM": return".palm"; + case"PBM": return".pbm"; + case"PCD": return".pcd"; + case"PCDS": return".pcd"; + case"PCL": return".pcl"; + case"PCT": return".pct"; + case"PCX": return".pcx"; + case"PDB": return".pdb"; + case"PDF": return".pdf"; + case"PDFA": return".pdf"; + case"PEF": return".pef"; + case"PFM": return".pfm"; + case"PGM": return".pgm"; + case"PGX": return".jp2"; + case"PICON": return".xpm"; + case"PICT": return".pct"; + case"PNG": return".png"; + case"PNG00": return".png"; + case"PNG24": return".png"; + case"PNG32": return".png"; + case"PNG48": return".png"; + case"PNG64": return".png"; + case"PNG8": return".png"; + case"PNM": return".pnm"; + case"PPM": return".ppm"; + case"PS": return".ps"; + case"PS2": return".ps"; + case"PS3": return".ps"; + case"PSB": return".psd"; + case"PSD": return".psd"; + case"PTIF": return".tif"; + case"RAF": return".raf"; + case"RAS": return".ras"; + case"RAW": return".dng"; + case"RLA": return".rla"; + case"RMF": return".rmf"; + case"RW2": return".rw2"; + case"SCR": return".scr"; + case"SGI": return".sgi"; + case"SR2": return".sr2"; + case"SRF": return".srf"; + case"SUN": return".ras"; + case"SVG": return".svg"; + case"SVGZ": return".svgz"; + case"TGA": return".tga"; + case"TIFF": return".tif"; + case"TIFF64": return".tif"; + case"TIM": return".tim"; + case"TIM2": return".tm2"; + case"TM2": return".tm2"; + case"VIFF": return".viff"; + case"VST": return".vst"; + case"WBMP": return".wbmp"; + case"WEBP": return".webp"; + case"WMF": return".wmf"; + case"WMZ": return".wmf"; + case"WPG": return".wpg"; + case"X": return".x"; + case"X3F": return".x3f"; + case"XBM": return".xbm"; + case"XC": return".xc"; + case"XCF": return".xcf"; + case"XPM": return".xpm"; + case"XPS": return".xps"; + case"XV": return".xv"; + case"XWD": return".xwd"; + default: return null; + } + } + } +} \ No newline at end of file diff --git a/Marechai/Helpers/Photos.cs b/Marechai/Helpers/Photos.cs new file mode 100644 index 00000000..1bac4c3e --- /dev/null +++ b/Marechai/Helpers/Photos.cs @@ -0,0 +1,346 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Marechai.Helpers +{ + public class Photos + { + public delegate Task ConversionFinished(bool result); + + public static void EnsureCreated(string webRootPath) + { + List paths = new List(); + + string photosRoot = Path.Combine(webRootPath, "assets", "photos"); + string machinePhotosRoot = Path.Combine(photosRoot, "machines"); + string machineThumbsRoot = Path.Combine(machinePhotosRoot, "thumbs"); + string machineOriginalPhotosRoot = Path.Combine(machinePhotosRoot, "originals"); + + paths.Add(photosRoot); + paths.Add(machinePhotosRoot); + paths.Add(machineThumbsRoot); + paths.Add(machineOriginalPhotosRoot); + + paths.Add(Path.Combine(machineThumbsRoot, "jpeg", "hd")); + paths.Add(Path.Combine(machineThumbsRoot, "jpeg", "1440p")); + paths.Add(Path.Combine(machineThumbsRoot, "jpeg", "4k")); + paths.Add(Path.Combine(machinePhotosRoot, "jpeg", "hd")); + paths.Add(Path.Combine(machinePhotosRoot, "jpeg", "1440p")); + paths.Add(Path.Combine(machinePhotosRoot, "jpeg", "4k")); + + paths.Add(Path.Combine(machineThumbsRoot, "jp2k", "hd")); + paths.Add(Path.Combine(machineThumbsRoot, "jp2k", "1440p")); + paths.Add(Path.Combine(machineThumbsRoot, "jp2k", "4k")); + paths.Add(Path.Combine(machinePhotosRoot, "jp2k", "hd")); + paths.Add(Path.Combine(machinePhotosRoot, "jp2k", "1440p")); + paths.Add(Path.Combine(machinePhotosRoot, "jp2k", "4k")); + + paths.Add(Path.Combine(machineThumbsRoot, "webp", "hd")); + paths.Add(Path.Combine(machineThumbsRoot, "webp", "1440p")); + paths.Add(Path.Combine(machineThumbsRoot, "webp", "4k")); + paths.Add(Path.Combine(machinePhotosRoot, "webp", "hd")); + paths.Add(Path.Combine(machinePhotosRoot, "webp", "1440p")); + paths.Add(Path.Combine(machinePhotosRoot, "webp", "4k")); + + paths.Add(Path.Combine(machineThumbsRoot, "heif", "hd")); + paths.Add(Path.Combine(machineThumbsRoot, "heif", "1440p")); + paths.Add(Path.Combine(machineThumbsRoot, "heif", "4k")); + paths.Add(Path.Combine(machinePhotosRoot, "heif", "hd")); + paths.Add(Path.Combine(machinePhotosRoot, "heif", "1440p")); + paths.Add(Path.Combine(machinePhotosRoot, "heif", "4k")); + + paths.Add(Path.Combine(machineThumbsRoot, "avif", "hd")); + paths.Add(Path.Combine(machineThumbsRoot, "avif", "1440p")); + paths.Add(Path.Combine(machineThumbsRoot, "avif", "4k")); + paths.Add(Path.Combine(machinePhotosRoot, "avif", "hd")); + paths.Add(Path.Combine(machinePhotosRoot, "avif", "1440p")); + paths.Add(Path.Combine(machinePhotosRoot, "avif", "4k")); + + foreach(string path in paths.Where(path => !Directory.Exists(path))) + Directory.CreateDirectory(path); + } + + public static bool Convert(string webRootPath, Guid id, string originalPath, string sourceFormat, + string outputFormat, string resolution, bool thumbnail) + { + outputFormat = outputFormat.ToLowerInvariant(); + resolution = resolution.ToLowerInvariant(); + sourceFormat = sourceFormat.ToLowerInvariant(); + + string outputPath = Path.Combine(webRootPath, "assets", "photos", "machines"); + int width, height; + + if(thumbnail) + outputPath = Path.Combine(outputPath, "thumbs"); + + outputPath = Path.Combine(outputPath, outputFormat); + outputPath = Path.Combine(outputPath, resolution); + + switch(resolution) + { + case"hd": + if(thumbnail) + { + width = 256; + height = 256; + } + else + { + width = 1920; + height = 1080; + } + + break; + case"1440p": + if(thumbnail) + { + width = 384; + height = 384; + } + else + { + width = 2560; + height = 1440; + } + + break; + case"4k": + if(thumbnail) + { + width = 512; + height = 512; + } + else + { + width = 3840; + height = 2160; + } + + break; + default: return false; + } + + string tmpPath; + bool ret; + + switch(outputFormat) + { + case"jpeg": + outputPath = Path.Combine(outputPath, $"{id}.jpg"); + + return ConvertUsingImageMagick(originalPath, outputPath, width, height); + case"jp2k": + outputPath = Path.Combine(outputPath, $"{id}.jp2"); + + return ConvertUsingImageMagick(originalPath, outputPath, width, height); + + case"webp": + outputPath = Path.Combine(outputPath, $"{id}.webp"); + + return ConvertUsingImageMagick(originalPath, outputPath, width, height); + + case"heif": + outputPath = Path.Combine(outputPath, $"{id}.heic"); + + return ConvertUsingImageMagick(originalPath, outputPath, width, height); + + case"avif": + outputPath = Path.Combine(outputPath, $"{id}.avif"); + + tmpPath = Path.GetTempFileName(); + File.Delete(tmpPath); + tmpPath += ".png"; + + // AVIFENC does not resize + ret = ConvertUsingImageMagick(originalPath, tmpPath, width, height); + + if(!ret) + { + File.Delete(tmpPath); + + return ret; + } + + ret = ConvertToAvif(tmpPath, outputPath, width, height); + + File.Delete(tmpPath); + + return ret; + default: return false; + } + } + + public static bool ConvertUsingImageMagick(string originalPath, string outputPath, int width, int height) + { + var convert = new Process + { + StartInfo = + { + FileName = "convert", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true, ArgumentList = + { + "-resize", $"{width}x{height}", "-strip", originalPath, + outputPath + } + } + }; + + try + { + convert.Start(); + convert.StandardOutput.ReadToEnd(); + convert.WaitForExit(); + + return convert.ExitCode == 0; + } + catch(Exception) + { + return false; + } + } + + public static bool ConvertToAvif(string originalPath, string outputPath, int width, int height) + { + var avif = new Process + { + StartInfo = + { + FileName = "avifenc", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true, ArgumentList = + { + "-j", "4", originalPath, outputPath + } + } + }; + + try + { + avif.Start(); + avif.StandardOutput.ReadToEnd(); + avif.WaitForExit(); + + return avif.ExitCode == 0; + } + catch(Exception) + { + return false; + } + } + + public void ConversionWorker(string webRootPath, Guid id, string originalFilePath, string sourceFormat) + { + List pool = new List + { + new Task(() => FinishedRenderingJpeg4kThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JPEG", "4k", true))), + new Task(() => FinishedRenderingJpeg1440Thumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JPEG", "1440p", + true))), + new Task(() => FinishedRenderingJpegHdThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JPEG", "hd", true))), + new Task(() => FinishedRenderingJpeg4k?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "JPEG", "4k", false))), + new Task(() => FinishedRenderingJpeg1440?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JPEG", "1440p", false))), + new Task(() => FinishedRenderingJpegHd?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "JPEG", "hd", false))), + new Task(() => FinishedRenderingJp2k4kThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JP2K", "4k", true))), + new Task(() => FinishedRenderingJp2k1440Thumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JP2K", "1440p", + true))), + new Task(() => FinishedRenderingJp2kHdThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JP2K", "hd", true))), + new Task(() => FinishedRenderingJp2k4k?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "JP2K", "4k", false))), + new Task(() => FinishedRenderingJp2k1440?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "JP2K", "1440p", false))), + new Task(() => FinishedRenderingJp2kHd?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "JP2K", "hd", false))), + new Task(() => FinishedRenderingWebp4kThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "WEBP", "4k", true))), + new Task(() => FinishedRenderingWebp1440Thumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "WEBP", "1440p", + true))), + new Task(() => FinishedRenderingWebpHdThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "WEBP", "hd", true))), + new Task(() => FinishedRenderingWebp4k?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "WEBP", "4k", false))), + new Task(() => FinishedRenderingWebp1440?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "WEBP", "1440p", false))), + new Task(() => FinishedRenderingWebpHd?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "WEBP", "hd", false))), + new Task(() => FinishedRenderingHeif4kThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "HEIF", "4k", true))), + new Task(() => FinishedRenderingHeif1440Thumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "HEIF", "1440p", + true))), + new Task(() => FinishedRenderingHeifHdThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "HEIF", "hd", true))), + new Task(() => FinishedRenderingHeif4k?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "HEIF", "4k", false))), + new Task(() => FinishedRenderingHeif1440?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "HEIF", "1440p", false))), + new Task(() => FinishedRenderingHeifHd?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "HEIF", "hd", false))), + new Task(() => FinishedRenderingAvif4kThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "AVIF", "4k", true))), + new Task(() => FinishedRenderingAvif1440Thumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "AVIF", "1440p", + true))), + new Task(() => FinishedRenderingAvifHdThumbnail?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "AVIF", "hd", true))), + new Task(() => FinishedRenderingAvif4k?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "AVIF", "4k", false))), + new Task(() => FinishedRenderingAvif1440?.Invoke(Convert(webRootPath, id, originalFilePath, + sourceFormat, "AVIF", "1440p", false))), + new Task(() => FinishedRenderingAvifHd?.Invoke(Convert(webRootPath, id, originalFilePath, sourceFormat, + "AVIF", "hd", false))) + }; + + foreach(Task thread in pool) + thread.Start(); + + Task.WaitAll(pool.ToArray()); + + FinishedAll?.Invoke(true); + } + + public event ConversionFinished FinishedAll; + + public event ConversionFinished FinishedRenderingJpegHdThumbnail; + public event ConversionFinished FinishedRenderingJpeg1440Thumbnail; + public event ConversionFinished FinishedRenderingJpeg4kThumbnail; + public event ConversionFinished FinishedRenderingJpegHd; + public event ConversionFinished FinishedRenderingJpeg1440; + public event ConversionFinished FinishedRenderingJpeg4k; + public event ConversionFinished FinishedRenderingJp2kHdThumbnail; + public event ConversionFinished FinishedRenderingJp2k1440Thumbnail; + public event ConversionFinished FinishedRenderingJp2k4kThumbnail; + public event ConversionFinished FinishedRenderingJp2kHd; + public event ConversionFinished FinishedRenderingJp2k1440; + public event ConversionFinished FinishedRenderingJp2k4k; + public event ConversionFinished FinishedRenderingWebpHdThumbnail; + public event ConversionFinished FinishedRenderingWebp1440Thumbnail; + public event ConversionFinished FinishedRenderingWebp4kThumbnail; + public event ConversionFinished FinishedRenderingWebpHd; + public event ConversionFinished FinishedRenderingWebp1440; + public event ConversionFinished FinishedRenderingWebp4k; + public event ConversionFinished FinishedRenderingHeifHdThumbnail; + public event ConversionFinished FinishedRenderingHeif1440Thumbnail; + public event ConversionFinished FinishedRenderingHeif4kThumbnail; + public event ConversionFinished FinishedRenderingHeifHd; + public event ConversionFinished FinishedRenderingHeif1440; + public event ConversionFinished FinishedRenderingHeif4k; + public event ConversionFinished FinishedRenderingAvifHdThumbnail; + public event ConversionFinished FinishedRenderingAvif1440Thumbnail; + public event ConversionFinished FinishedRenderingAvif4kThumbnail; + public event ConversionFinished FinishedRenderingAvifHd; + public event ConversionFinished FinishedRenderingAvif1440; + public event ConversionFinished FinishedRenderingAvif4k; + } +} \ No newline at end of file diff --git a/Marechai/Marechai.csproj b/Marechai/Marechai.csproj index 5380d084..2eac4b5c 100644 --- a/Marechai/Marechai.csproj +++ b/Marechai/Marechai.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 3.0.99.1405 + 3.0.99.1489 Canary Islands Computer Museum Copyright © 2003-2020 Natalia Portillo Canary Islands Computer Museum Website @@ -131,6 +131,9 @@ true + + true + <_ContentIncludedByDefault Remove="Areas\Admin\Views\BrowserTests\Delete.cshtml" /> @@ -203,5 +206,10 @@ <_ContentIncludedByDefault Remove="Areas\Admin\Views\ResolutionsByScreen\Details.cshtml" /> <_ContentIncludedByDefault Remove="Areas\Admin\Views\ResolutionsByScreen\Edit.cshtml" /> <_ContentIncludedByDefault Remove="Areas\Admin\Views\ResolutionsByScreen\Index.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Admin\Views\MachinePhotos\Create.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Admin\Views\MachinePhotos\Delete.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Admin\Views\MachinePhotos\Details.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Admin\Views\MachinePhotos\Edit.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Admin\Views\MachinePhotos\Index.cshtml" /> \ No newline at end of file diff --git a/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor b/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor new file mode 100644 index 00000000..8ded8a0c --- /dev/null +++ b/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor @@ -0,0 +1,856 @@ +@{ +/****************************************************************************** +// MARECHAI: Master repository of computing history artifacts information +// ---------------------------------------------------------------------------- +// +// Filename : Details.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Admin view details +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2020 Natalia Portillo +*******************************************************************************/ +} + +@page "/admin/machines/photo/create/{MachineId:int}" +@using Marechai.Database +@using Marechai.Database.Models +@inherits OwningComponentBase +@inject IStringLocalizer L +@inject NavigationManager NavigationManager +@inject LicensesService LicensesService +@inject IFileReaderService FileReaderService; +@inject MachinesService MachinesService; +@inject Microsoft.AspNetCore.Identity.UserManager UserManager +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject IWebHostEnvironment Host +@attribute [Authorize(Roles = "UberAdmin, Admin")] + +@if (!_loaded) +{ +

@L["Loading..."]

+ + return; +} + +

@string.Format(L["Upload photo for machine {0} manufactured by {1}"], _machine.Name, _machine.Company)

+
+ +@if(!_uploaded) +{ + @if(!_uploading) + { + @L["Choose photo file"] +
+ + @L["License"] + + + + @L["Source URL"] + @L["Unknown (source url)"] + @if (!_unknownSource) + { + + + + @L["Please enter a valid source URL."] + + + + } + +
TODO: Put legal disclaimer here
+
+ } + else + { + @L["Uploading..."] +
+
@($"{_progressValue:F2}")%
+
+ } + @if(_uploadError) + { + @_uploadErrorMessage + } +} +else +{ +
@string.Format(L["Image format recognized as {0}"], _imageFormat)
+ @if (_allFinished) + { + @L["All finished!"] + @L["Go to photo details"] + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ @L["Extracting Exif information..."] + + @if(_extractExif == true) + { + @L["OK!"] + } + else if(_extractExif == false) + { + @L["Error!"] + } +
+ @L["Moving file to proper place..."] + + @if(_moveFile == true) + { + @L["OK!"] + } + else if(_moveFile == false) + { + @L["Error!"] + } + + @if(_moveFile == true) + { + + + + } +
+ @L["Converting photo to JPEG with an HD resolution (thumbnail)..."] + + @if(_convertJpegHdTh == true) + { + @L["OK!"] + } + else if(_convertJpegHdTh == false) + { + @L["Error!"] + } + + @if(_convertJpegHdTh == true) + { + + + + } +
+ @L["Converting photo to JPEG with a 1440p resolution (thumbnail)..."] + + @if(_convertJpeg1440Th == true) + { + @L["OK!"] + } + else if(_convertJpeg1440Th == false) + { + @L["Error!"] + } + + @if(_convertJpeg1440Th == true) + { + + + + } +
+ @L["Converting photo to JPEG with a 4K resolution (thumbnail)..."] + + @if(_convertJpeg4kTh == true) + { + @L["OK!"] + } + else if(_convertJpeg4kTh == false) + { + @L["Error!"] + } + + @if(_convertJpeg4kTh == true) + { + + + + } +
+ @L["Converting photo to JPEG with an HD resolution..."] + + @if(_convertJpegHd == true) + { + @L["OK!"] + } + else if(_convertJpegHd == false) + { + @L["Error!"] + } + + @if(_convertJpegHd == true) + { + + + + } +
+ @L["Converting photo to JPEG with a 1440p resolution..."] + + @if(_convertJpeg1440 == true) + { + @L["OK!"] + } + else if(_convertJpeg1440 == false) + { + @L["Error!"] + } + + @if(_convertJpeg1440 == true) + { + + + + } +
+ @L["Converting photo to JPEG with a 4K resolution..."] + + @if(_convertJpeg4k == true) + { + @L["OK!"] + } + else if(_convertJpeg4k == false) + { + @L["Error!"] + } + + @if(_convertJpeg4k == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with an HD resolution (thumbnail)..."] + + @if(_convertJp2kHdTh == true) + { + @L["OK!"] + } + else if(_convertJp2kHdTh == false) + { + @L["Error!"] + } + + @if(_convertJp2kHdTh == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with a 1440p resolution (thumbnail)..."] + + @if(_convertJp2k1440Th == true) + { + @L["OK!"] + } + else if(_convertJp2k1440Th == false) + { + @L["Error!"] + } + + @if(_convertJp2k1440Th == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with a 4K resolution (thumbnail)..."] + + @if(_convertJp2k4kTh == true) + { + @L["OK!"] + } + else if(_convertJp2k4kTh == false) + { + @L["Error!"] + } + + @if(_convertJp2k4kTh == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with an HD resolution..."] + + @if(_convertJp2kHd == true) + { + @L["OK!"] + } + else if(_convertJp2kHd == false) + { + @L["Error!"] + } + + @if(_convertJp2kHd == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with a 1440p resolution..."] + + @if(_convertJp2k1440 == true) + { + @L["OK!"] + } + else if(_convertJp2k1440 == false) + { + @L["Error!"] + } + + @if(_convertJp2k1440 == true) + { + + + + } +
+ @L["Converting photo to JPEG2000 with a 4K resolution..."] + + @if(_convertJp2k4k == true) + { + @L["OK!"] + } + else if(_convertJp2k4k == false) + { + @L["Error!"] + } + + @if(_convertJp2k4k == true) + { + + + + } +
+ @L["Converting photo to WebP with an HD resolution (thumbnail)..."] + + @if(_convertWebpHdTh == true) + { + @L["OK!"] + } + else if(_convertWebpHdTh == false) + { + @L["Error!"] + } + + @if(_convertWebpHdTh == true) + { + + + + } +
+ @L["Converting photo to WebP with a 1440p resolution (thumbnail)..."] + + @if(_convertWebp1440Th == true) + { + @L["OK!"] + } + else if(_convertWebp1440Th == false) + { + @L["Error!"] + } + + @if(_convertWebp1440Th == true) + { + + + + } +
+ @L["Converting photo to WebP with a 4K resolution (thumbnail)..."] + + @if(_convertWebp4kTh == true) + { + @L["OK!"] + } + else if(_convertWebp4kTh == false) + { + @L["Error!"] + } + + @if(_convertWebp4kTh == true) + { + + + + } +
+ @L["Converting photo to WebP with an HD resolution..."] + + @if(_convertWebpHd == true) + { + @L["OK!"] + } + else if(_convertWebpHd == false) + { + @L["Error!"] + } + + @if(_convertWebpHd == true) + { + + + + } +
+ @L["Converting photo to WebP with a 1440p resolution..."] + + @if(_convertWebp1440 == true) + { + @L["OK!"] + } + else if(_convertWebp1440 == false) + { + @L["Error!"] + } + + @if(_convertWebp1440 == true) + { + + + + } +
+ @L["Converting photo to WebP with a 4K resolution..."] + + @if(_convertWebp4k == true) + { + @L["OK!"] + } + else if(_convertWebp4k == false) + { + @L["Error!"] + } + + @if(_convertWebp4k == true) + { + + + + } +
+ @L["Converting photo to HEIF with an HD resolution (thumbnail)..."] + + @if(_convertHeifHdTh == true) + { + @L["OK!"] + } + else if(_convertHeifHdTh == false) + { + @L["Error!"] + } + + @if(_convertHeifHdTh == true) + { + + + + } +
+ @L["Converting photo to HEIF with a 1440p resolution (thumbnail)..."] + + @if(_convertHeif1440Th == true) + { + @L["OK!"] + } + else if(_convertHeif1440Th == false) + { + @L["Error!"] + } + + @if(_convertHeif1440Th == true) + { + + + + } +
+ @L["Converting photo to HEIF with a 4K resolution (thumbnail)..."] + + @if(_convertHeif4kTh == true) + { + @L["OK!"] + } + else if(_convertHeif4kTh == false) + { + @L["Error!"] + } + + @if(_convertHeif4kTh == true) + { + + + + } +
+ @L["Converting photo to HEIF with an HD resolution..."] + + @if(_convertHeifHd == true) + { + @L["OK!"] + } + else if(_convertHeifHd == false) + { + @L["Error!"] + } + + @if(_convertHeifHd == true) + { + + + + } +
+ @L["Converting photo to HEIF with a 1440p resolution..."] + + @if(_convertHeif1440 == true) + { + @L["OK!"] + } + else if(_convertHeif1440 == false) + { + @L["Error!"] + } + + @if(_convertHeif1440 == true) + { + + + + } +
+ @L["Converting photo to HEIF with a 4K resolution..."] + + @if(_convertHeif4k == true) + { + @L["OK!"] + } + else if(_convertHeif4k == false) + { + @L["Error!"] + } + + @if(_convertHeif4k == true) + { + + + + } +
+ @L["Converting photo to AV1F with an HD resolution (thumbnail)..."] + + @if(_convertAvifHdTh == true) + { + @L["OK!"] + } + else if(_convertAvifHdTh == false) + { + @L["Error!"] + } + + @if(_convertAvifHdTh == true) + { + + + + } +
+ @L["Converting photo to AV1F with a 1440p resolution (thumbnail)..."] + + @if(_convertAvif1440Th == true) + { + @L["OK!"] + } + else if(_convertAvif1440Th == false) + { + @L["Error!"] + } + + @if(_convertAvif1440Th == true) + { + + + + } +
+ @L["Converting photo to AV1F with a 4K resolution (thumbnail)..."] + + @if(_convertAvif4kTh == true) + { + @L["OK!"] + } + else if(_convertAvif4kTh == false) + { + @L["Error!"] + } + + @if(_convertAvif4kTh == true) + { + + + + } +
+ @L["Converting photo to AV1F with an HD resolution..."] + + @if(_convertAvifHd == true) + { + @L["OK!"] + } + else if(_convertAvifHd == false) + { + @L["Error!"] + } + + @if(_convertAvifHd == true) + { + + + + } +
+ @L["Converting photo to AV1F with a 1440p resolution..."] + + @if(_convertAvif1440 == true) + { + @L["OK!"] + } + else if(_convertAvif1440 == false) + { + @L["Error!"] + } + + @if(_convertAvif1440 == true) + { + + + + } +
+ @L["Converting photo to AV1F with a 4K resolution..."] + + @if(_convertAvif4k == true) + { + @L["OK!"] + } + else if(_convertAvif4k == false) + { + @L["Error!"] + } + + @if(_convertAvif4k == true) + { + + + + } +
+ @L["Adding photo to database..."] + + @if(_addToDatabase == true) + { + @L["OK!"] + } + else if(_addToDatabase == false) + { + @L["Error!"] + } + +
+} diff --git a/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor.cs b/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor.cs new file mode 100644 index 00000000..20922960 --- /dev/null +++ b/Marechai/Pages/Admin/Details/CreateMachinePhoto.razor.cs @@ -0,0 +1,614 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Blazor.FileReader; +using Blazorise; +using Marechai.Helpers; +using Marechai.Shared; +using Marechai.ViewModels; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; + +namespace Marechai.Pages.Admin.Details +{ + public partial class CreateMachinePhoto + { + const int _maxUploadSize = 25 * 1048576; + bool? _addToDatabase; + bool _allFinished; + AuthenticationState _authState; + bool? _convertAvif1440; + bool? _convertAvif1440Th; + bool? _convertAvif4k; + bool? _convertAvif4kTh; + bool? _convertAvifHd; + bool? _convertAvifHdTh; + bool? _convertHeif1440; + bool? _convertHeif1440Th; + bool? _convertHeif4k; + bool? _convertHeif4kTh; + bool? _convertHeifHd; + bool? _convertHeifHdTh; + bool? _convertJp2k1440; + bool? _convertJp2k1440Th; + bool? _convertJp2k4k; + bool? _convertJp2k4kTh; + bool? _convertJp2kHd; + bool? _convertJp2kHdTh; + bool? _convertJpeg1440; + bool? _convertJpeg1440Th; + bool? _convertJpeg4k; + bool? _convertJpeg4kTh; + bool? _convertJpegHd; + bool? _convertJpegHdTh; + bool? _convertWebp1440; + bool? _convertWebp1440Th; + bool? _convertWebp4k; + bool? _convertWebp4kTh; + bool? _convertWebpHd; + bool? _convertWebpHdTh; + bool? _extractExif; + string _imageFormat; + ElementReference _inputUpload; + int _licenseId; + List _licenses; + bool _loaded; + MachineViewModel _machine; + MachinePhotoViewModel _model; + bool? _moveFile; + double _progressValue; + string _sourceUrl; + bool _unknownSource; + bool _uploaded; + bool _uploadError; + string _uploadErrorMessage; + bool _uploading; + [Parameter] + public Guid Id { get; set; } + [Parameter] + public int MachineId { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(_loaded) + return; + + _licenses = await LicensesService.GetAsync(); + _machine = await MachinesService.GetAsync(MachineId); + _authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + + if(_machine is null) + NavigationManager.ToBaseRelativePath("admin/machines"); + + _loaded = true; + + StateHasChanged(); + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + async Task UploadFile() + { + if(!_unknownSource && + string.IsNullOrWhiteSpace(_sourceUrl)) + return; + + if(_licenseId == 0) + { + _uploadError = true; + _uploadErrorMessage = L["Please choose a valid license."]; + + return; + } + + var processExiftool = new Process + { + StartInfo = + { + FileName = "exiftool", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true + } + }; + + var processIdentify = new Process + { + StartInfo = + { + FileName = "identify", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true + } + }; + + var processConvert = new Process + { + StartInfo = + { + FileName = "convert", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true + } + }; + + string identifyOutput; + string convertOutput; + string exiftoolOutput; + + try + { + processIdentify.Start(); + identifyOutput = await processIdentify.StandardOutput.ReadToEndAsync(); + processIdentify.WaitForExit(); + processConvert.Start(); + convertOutput = await processConvert.StandardOutput.ReadToEndAsync(); + processConvert.WaitForExit(); + processExiftool.Start(); + exiftoolOutput = await processExiftool.StandardOutput.ReadToEndAsync(); + processExiftool.WaitForExit(); + } + catch(Exception) + { + _uploadError = true; + _uploadErrorMessage = L["Cannot run ImageMagick please contact the administrator."]; + + return; + } + + IFileReference file = (await FileReaderService.CreateReference(_inputUpload).EnumerateFilesAsync()). + FirstOrDefault(); + + if(file is null) + return; + + IFileInfo fileInfo = await file.ReadFileInfoAsync(); + + if(fileInfo.Size > _maxUploadSize) + { + _uploadError = true; + _uploadErrorMessage = L["The selected file is too big."]; + + return; + } + + string tmpPath = Path.GetTempFileName(); + + FileStream outFs; + + try + { + outFs = new FileStream(tmpPath, FileMode.Open, FileAccess.ReadWrite); + } + catch(Exception) + { + _uploadError = true; + _uploadErrorMessage = L["There was an error uploading the file."]; + + return; + } + + _uploading = true; + + await using AsyncDisposableStream fs = await file.OpenReadAsync(); + byte[] buffer = new byte[20480]; + + try + { + double lastProgress = 0; + int count; + + while((count = await fs.ReadAsync(buffer, 0, buffer.Length)) != 0) + { + await outFs.WriteAsync(buffer, 0, count); + + double progress = ((double)fs.Position * 100) / fs.Length; + + if(!(progress > lastProgress + 0.01)) + continue; + + _progressValue = progress; + await InvokeAsync(StateHasChanged); + await Task.Yield(); + lastProgress = progress; + } + } + catch(Exception) + { + _uploading = false; + _uploadError = true; + _uploadErrorMessage = L["There was an error uploading the file."]; + + return; + } + + outFs.Close(); + _uploading = false; + await InvokeAsync(StateHasChanged); + await Task.Yield(); + + processIdentify = new Process + { + StartInfo = + { + FileName = "identify", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true, ArgumentList = + { + tmpPath + } + } + }; + + processIdentify.Start(); + identifyOutput = await processIdentify.StandardOutput.ReadToEndAsync(); + processIdentify.WaitForExit(); + + if(processIdentify.ExitCode != 0 || + string.IsNullOrWhiteSpace(identifyOutput)) + { + _uploading = false; + _uploadError = true; + _uploadErrorMessage = L["The uploaded file was not recognized as an image."]; + File.Delete(tmpPath); + + return; + } + + string[] pieces = identifyOutput.Substring(tmpPath.Length). + Split(" ", StringSplitOptions.RemoveEmptyEntries); + + if(pieces.Length < 2) + { + _uploading = false; + _uploadError = true; + _uploadErrorMessage = L["The uploaded file was not recognized as an image."]; + File.Delete(tmpPath); + + return; + } + + // TODO: Move this to Helpers, keep progress + + string extension = ImageMagick.GetExtension(pieces[0]); + + if(string.IsNullOrWhiteSpace(extension)) + { + _uploading = false; + _uploadError = true; + _uploadErrorMessage = L["The uploaded file was not recognized as an image."]; + File.Delete(tmpPath); + + return; + } + + _imageFormat = pieces[0]; + _uploaded = true; + + _model = new MachinePhotoViewModel + { + UserId = (await UserManager.GetUserAsync(_authState.User)).Id, MachineId = MachineId, + Id = Guid.NewGuid(), OriginalExtension = extension, UploadDate = DateTime.UtcNow, + Source = _unknownSource ? null : _sourceUrl, LicenseId = _licenseId + }; + + try + { + processExiftool = new Process + { + StartInfo = + { + FileName = "exiftool", CreateNoWindow = true, RedirectStandardError = true, + RedirectStandardOutput = true, ArgumentList = + { + "-n", "-json", tmpPath + } + } + }; + + processExiftool.Start(); + exiftoolOutput = await processExiftool.StandardOutput.ReadToEndAsync(); + processExiftool.WaitForExit(); + + Exif[] exif = JsonSerializer.Deserialize(exiftoolOutput); + + if(exif?.Length >= 1) + exif[0].ToViewModel(_model); + + _extractExif = true; + } + catch(Exception) + { + _extractExif = false; + } + + string originalFilePath = Path.Combine(Host.WebRootPath, "assets", "photos", "machines", "originals", + $"{_model.Id}{_model.OriginalExtension}"); + + try + { + File.Move(tmpPath, originalFilePath); + _moveFile = true; + } + catch(Exception) + { + _moveFile = false; + File.Delete(tmpPath); + + return; + } + + await Task.Yield(); + await InvokeAsync(StateHasChanged); + + var photos = new Photos(); + photos.FinishedAll += OnFinishedAll; + photos.FinishedRenderingJpeg4k += OnFinishedRenderingJpeg4k; + photos.FinishedRenderingJpeg1440 += OnFinishedRenderingJpeg1440; + photos.FinishedRenderingJpegHd += OnFinishedRenderingJpegHd; + photos.FinishedRenderingJpeg4kThumbnail += OnFinishedRenderingJpeg4kThumbnail; + photos.FinishedRenderingJpeg1440Thumbnail += OnFinishedRenderingJpeg1440Thumbnail; + photos.FinishedRenderingJpegHdThumbnail += OnFinishedRenderingJpegHdThumbnail; + photos.FinishedRenderingJp2k4k += OnFinishedRenderingJp2k4k; + photos.FinishedRenderingJp2k1440 += OnFinishedRenderingJp2k1440; + photos.FinishedRenderingJp2kHd += OnFinishedRenderingJp2kHd; + photos.FinishedRenderingJp2k4kThumbnail += OnFinishedRenderingJp2k4kThumbnail; + photos.FinishedRenderingJp2k1440Thumbnail += OnFinishedRenderingJp2k1440Thumbnail; + photos.FinishedRenderingJp2kHdThumbnail += OnFinishedRenderingJp2kHdThumbnail; + photos.FinishedRenderingWebp4k += OnFinishedRenderingWebp4k; + photos.FinishedRenderingWebp1440 += OnFinishedRenderingWebp1440; + photos.FinishedRenderingWebpHd += OnFinishedRenderingWebpHd; + photos.FinishedRenderingWebp4kThumbnail += OnFinishedRenderingWebp4kThumbnail; + photos.FinishedRenderingWebp1440Thumbnail += OnFinishedRenderingWebp1440Thumbnail; + photos.FinishedRenderingWebpHdThumbnail += OnFinishedRenderingWebpHdThumbnail; + photos.FinishedRenderingHeif4k += OnFinishedRenderingHeif4k; + photos.FinishedRenderingHeif1440 += OnFinishedRenderingHeif1440; + photos.FinishedRenderingHeifHd += OnFinishedRenderingHeifHd; + photos.FinishedRenderingHeif4kThumbnail += OnFinishedRenderingHeif4kThumbnail; + photos.FinishedRenderingHeif1440Thumbnail += OnFinishedRenderingHeif1440Thumbnail; + photos.FinishedRenderingHeifHdThumbnail += OnFinishedRenderingHeifHdThumbnail; + photos.FinishedRenderingAvif4k += OnFinishedRenderingAvif4k; + photos.FinishedRenderingAvif1440 += OnFinishedRenderingAvif1440; + photos.FinishedRenderingAvifHd += OnFinishedRenderingAvifHd; + photos.FinishedRenderingAvif4kThumbnail += OnFinishedRenderingAvif4kThumbnail; + photos.FinishedRenderingAvif1440Thumbnail += OnFinishedRenderingAvif1440Thumbnail; + photos.FinishedRenderingAvifHdThumbnail += OnFinishedRenderingAvifHdThumbnail; + + #pragma warning disable 4014 + Task.Run(() => photos.ConversionWorker(Host.WebRootPath, _model.Id, originalFilePath, _imageFormat)); + #pragma warning restore 4014 + } + + async Task OnFinishedRenderingJpeg4k(bool result) + { + _convertJpeg4k = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJpeg1440(bool result) + { + _convertJpeg1440 = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJpegHd(bool result) + { + _convertJpegHd = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJpeg4kThumbnail(bool result) + { + _convertJpeg4kTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJpeg1440Thumbnail(bool result) + { + _convertJpeg1440Th = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJpegHdThumbnail(bool result) + { + _convertJpegHdTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2k4k(bool result) + { + _convertJp2k4k = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2k1440(bool result) + { + _convertJp2k1440 = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2kHd(bool result) + { + _convertJp2kHd = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2k4kThumbnail(bool result) + { + _convertJp2k4kTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2k1440Thumbnail(bool result) + { + _convertJp2k1440Th = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingJp2kHdThumbnail(bool result) + { + _convertJp2kHdTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebp4k(bool result) + { + _convertWebp4k = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebp1440(bool result) + { + _convertWebp1440 = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebpHd(bool result) + { + _convertWebpHd = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebp4kThumbnail(bool result) + { + _convertWebp4kTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebp1440Thumbnail(bool result) + { + _convertWebp1440Th = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingWebpHdThumbnail(bool result) + { + _convertWebpHdTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeif4k(bool result) + { + _convertHeif4k = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeif1440(bool result) + { + _convertHeif1440 = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeifHd(bool result) + { + _convertHeifHd = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeif4kThumbnail(bool result) + { + _convertHeif4kTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeif1440Thumbnail(bool result) + { + _convertHeif1440Th = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingHeifHdThumbnail(bool result) + { + _convertHeifHdTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvif4k(bool result) + { + _convertAvif4k = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvif1440(bool result) + { + _convertAvif1440 = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvifHd(bool result) + { + _convertAvifHd = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvif4kThumbnail(bool result) + { + _convertAvif4kTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvif1440Thumbnail(bool result) + { + _convertAvif1440Th = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedRenderingAvifHdThumbnail(bool result) + { + _convertAvifHdTh = result; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + async Task OnFinishedAll(bool result) + { + try + { + await Service.CreateAsync(_model); + _addToDatabase = true; + } + catch(Exception e) + { + _addToDatabase = false; + Console.WriteLine(e); + + throw; + } + + _allFinished = true; + await Task.Yield(); + await InvokeAsync(StateHasChanged); + } + + void ValidateSource(ValidatorEventArgs e) => + Validators.ValidateUrl(e, L["Source URL must be smaller than 255 characters."], 255); + } +} \ No newline at end of file diff --git a/Marechai/Pages/Admin/Details/Machine.razor b/Marechai/Pages/Admin/Details/Machine.razor index dcdfd254..3023c20a 100644 --- a/Marechai/Pages/Admin/Details/Machine.razor +++ b/Marechai/Pages/Admin/Details/Machine.razor @@ -49,6 +49,7 @@ @inject StorageByMachineService StorageByMachineService @inject ScreensByMachineService ScreensByMachineService @inject ScreensService ScreensService +@inject MachinePhotosService MachinePhotosService @attribute [Authorize(Roles = "UberAdmin, Admin")]

@L["Machine details"]


diff --git a/Marechai/Pages/Admin/Details/Machine.razor.cs b/Marechai/Pages/Admin/Details/Machine.razor.cs index bfe34a61..5b0bd365 100644 --- a/Marechai/Pages/Admin/Details/Machine.razor.cs +++ b/Marechai/Pages/Admin/Details/Machine.razor.cs @@ -61,6 +61,7 @@ namespace Marechai.Pages.Admin.Details List _machineStorage; MachineViewModel _model; bool _noFamily; + List _photos; bool _prototype; bool _savingCpu; bool _savingGpu; @@ -111,6 +112,7 @@ namespace Marechai.Pages.Admin.Details _machineMemories = await MemoriesByMachineService.GetByMachine(Id); _machineStorage = await StorageByMachineService.GetByMachine(Id); _machineScreens = await ScreensByMachineService.GetByMachine(Id); + _photos = await MachinePhotosService.GetGuidsByMachineAsync(Id); _editing = _creating || NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLowerInvariant(). StartsWith("admin/machines/edit/", diff --git a/Marechai/Pages/Admin/Details/MachinePhoto.razor b/Marechai/Pages/Admin/Details/MachinePhoto.razor new file mode 100644 index 00000000..1efa4995 --- /dev/null +++ b/Marechai/Pages/Admin/Details/MachinePhoto.razor @@ -0,0 +1,769 @@ +@{ +/****************************************************************************** +// MARECHAI: Master repository of computing history artifacts information +// ---------------------------------------------------------------------------- +// +// Filename : Details.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Admin view details +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2020 Natalia Portillo +*******************************************************************************/ +} + +@page "/admin/machines/photo/details/{Id:guid}" +@page "/admin/machines/photo/edit/{Id:guid}" +@using Marechai.Database +@using Marechai.Database.Models +@inherits OwningComponentBase +@inject IStringLocalizer L +@inject NavigationManager NavigationManager +@inject LicensesService LicensesService +@inject Microsoft.AspNetCore.Identity.UserManager UserManager + +@attribute [Authorize(Roles = "UberAdmin, Admin")] +

@L["Machine photo details"]

+
+ +@if (!_loaded) +{ +

@L["Loading..."]

+ + return; +} + +
+ + @L["Machine"] + + + + @L["Uploaded by"] + ")"/> + + + @L["Uploaded date"] + + + + @L["License"] + + + @if (_editing || _model.Author != null) + { + + @L["Author"] + @if (_editing) + { + @L["Unknown (author)"] + } + @if (!_editing || + !_unknownSource) + { + + + + @L["Please enter a valid author."] + + + + } + + } + @if (_editing || _model.Source != null) + { + + @L["Source URL"] + @if (_editing) + { + @L["Unknown (source url)"] + } + @if (!_editing || + !_unknownSource) + { + + + + @L["Please enter a valid source URL."] + + + + } + + } + @if (_editing || _model.Aperture != null) + { + + @L["Aperture"] + @if (_editing) + { + @L["Unknown (aperture)"] + } + @if (!_editing || + !_unknownAperture) + { + + + + @L["Please enter a valid aperture."] + + + + } + + } + @if (_editing || _model.CameraManufacturer != null) + { + + @L["Camera manufacturer"] + @if (_editing) + { + @L["Unknown (camera manufacturer)"] + } + @if (!_editing || + !_unknownCameraManufacturer) + { + + + + @L["Please enter a valid camera manufacturer."] + + + + } + + } + @if (_editing || _model.CameraModel != null) + { + + @L["Camera model"] + @if (_editing) + { + @L["Unknown (camera model)"] + } + @if (!_editing || + !_unknownCameraModel) + { + + + + @L["Please enter a valid camera model."] + + + + } + + } + @if (_editing || _model.ColorSpace != null) + { + + @L["Color space"] + @if (_editing) + { + @L["Unknown (color space)"] + } + @if (!_editing || + !_unknownColorSpace) + { + + } + + } + @if (_editing || _model.Contrast != null) + { + + @L["Contrast"] + @if (_editing) + { + @L["Unknown (contrast)"] + } + @if (!_editing || + !_unknownContrast) + { + + } + + } + @if (_editing || _model.CreationDate != null) + { + + @L["Creation date"] + @if (_editing) + { + @L["Unknown (creation date)"] + } + @if (!_editing || + !_unknownCreationDate) + { + + + + @L["Please enter a correct creation date."] + + + + } + + } + @if (_editing || _model.DigitalZoomRatio != null) + { + + @L["Digital zoom ratio"] + @if (_editing) + { + @L["Unknown (digital zoom ratio)"] + } + @if (!_editing || + !_unknownDigitalZoomRatio) + { + + + + @L["Please enter a valid digital zoom ratio."] + + + + } + + } + @if (_editing || _model.ExifVersion != null) + { + + @L["Exif version"] + @if (_editing) + { + @L["Unknown (exif version)"] + } + @if (!_editing || + !_unknownExifVersion) + { + + + + @L["Please enter a valid Exif version."] + + + + } + + } + @if (_editing || _model.ExposureTime != null) + { + + @L["Exposure time"] + @if (_editing) + { + @L["Unknown (exposure time)"] + } + @if (!_editing || + !_unknownExposure) + { + + + + @L["Please enter a valid exposure time."] + + + + } + + } + @if (_editing || _model.ExposureMethod != null) + { + + @L["Exposure mode"] + @if (_editing) + { + @L["Unknown (exposure mode)"] + } + @if (!_editing || + !_unknownExposureMethod) + { + + } + + } + @if (_editing || _model.ExposureProgram != null) + { + + @L["Exposure program"] + @if (_editing) + { + @L["Unknown (exposure program)"] + } + @if (!_editing || + !_unknownExposureProgram) + { + + } + + } + @if (_editing || _model.Flash != null) + { + + @L["Flash"] + @if (_editing) + { + @L["Unknown (flash)"] + } + @if (!_editing || + !_unknownFlash) + { + + } + + } + @if (_editing || _model.Focal != null) + { + + @L["F-number"] + @if (_editing) + { + @L["Unknown (f-number)"] + } + @if (!_editing || + !_unknownFocal) + { + + + + @L["Please enter a valid focal number."] + + + + } + + } + @if (_editing || _model.FocalLength != null) + { + + @L["Focal length"] + @if (_editing) + { + @L["Unknown (focal length)"] + } + @if (!_editing || + !_unknownFocalLength) + { + + + + @L["Please enter a valid focal length."] + + + + } + + } + @if (_editing || _model.FocalLengthEquivalent != null) + { + + @L["Focal length in 35mm film"] + @if (_editing) + { + @L["Unknown (focal length in 35mm film)"] + } + @if (!_editing || + !_unknownFocalLengthEquivalent) + { + + + + @L["Please enter a valid focal length in 35mm film."] + + + + } + + } + @if (_editing || _model.HorizontalResolution != null) + { + + @L["Horizontal resolution"] + @if (_editing) + { + @L["Unknown (horizontal resolution)"] + } + @if (!_editing || + !_unknownHorizontalResolution) + { + + + + @L["Please enter a valid horizontal resolution."] + + + + } + + } + @if (_editing || _model.IsoRating != null) + { + + @L["ISO rating"] + @if (_editing) + { + @L["Unknown (ISO rating)"] + } + @if (!_editing || + !_unknownIsoRating) + { + + + + @L["Please enter a valid ISO rating."] + + + + } + + } + @if (_editing || _model.Lens != null) + { + + @L["Lens"] + @if (_editing) + { + @L["Unknown (lens)"] + } + @if (!_editing || + !_unknownLens) + { + + + + @L["Please enter a valid lens."] + + + + } + + } + @if (_editing || _model.LightSource != null) + { + + @L["Light source"] + @if (_editing) + { + @L["Unknown (light source)"] + } + @if (!_editing || + !_unknownLightSource) + { + + } + + } + @if (_editing || _model.MeteringMode != null) + { + + @L["Metering mode"] + @if (_editing) + { + @L["Unknown (metering mode)"] + } + @if (!_editing || + !_unknownMeteringMode) + { + + } + + } + @if (_editing || _model.ResolutionUnit != null) + { + + @L["Resolution unit"] + @if (_editing) + { + @L["Unknown (resolution unit)"] + } + @if (!_editing || + !_unknownResolutionUnit) + { + + } + + } + @if (_editing || _model.Orientation != null) + { + + @L["Orientation"] + @if (_editing) + { + @L["Unknown (orientation)"] + } + @if (!_editing || + !_unknownOrientation) + { + + } + + } + @if (_editing || _model.Saturation != null) + { + + @L["Saturation"] + @if (_editing) + { + @L["Unknown (saturation)"] + } + @if (!_editing || + !_unknownSaturation) + { + + } + + } + @if (_editing || _model.SceneCaptureType != null) + { + + @L["Scene capture type"] + @if (_editing) + { + @L["Unknown (scene capture type)"] + } + @if (!_editing || + !_unknownSceneCaptureType) + { + + } + + } + @if (_editing || _model.SensingMethod != null) + { + + @L["Sensing method"] + @if (_editing) + { + @L["Unknown (sensing method)"] + } + @if (!_editing || + !_unknownSensingMethod) + { + + } + + } + @if (_editing || _model.Sharpness != null) + { + + @L["Sharpness"] + @if (_editing) + { + @L["Unknown (sharpness)"] + } + @if (!_editing || + !_unknownSharpness) + { + + } + + } + @if (_editing || _model.SoftwareUsed != null) + { + + @L["Software used"] + @if (_editing) + { + @L["Unknown (software used)"] + } + @if (!_editing || + !_unknownSoftwareUsed) + { + + + + @L["Please enter a valid software used."] + + + + } + + } + @if (_editing || _model.SubjectDistanceRange != null) + { + + @L["Subject distance range"] + @if (_editing) + { + @L["Unknown (subject distance range)"] + } + @if (!_editing || + !_unknownSubjectDistanceRange) + { + + } + + } + @if (_editing || _model.VerticalResolution != null) + { + + @L["Vertical resolution"] + @if (_editing) + { + @L["Unknown (vertical resolution)"] + } + @if (!_editing || + !_unknownVerticalResolution) + { + + + + @L["Please enter a valid vertical resolution."] + + + + } + + } + @if (_editing || _model.WhiteBalance != null) + { + + @L["White balance"] + @if (_editing) + { + @L["Unknown (white balance)"] + } + @if (!_editing || + !_unknownWhiteBalance) + { + + } + + } + @if (_editing || _model.Comments != null) + { + + @L["User comments"] + @if (_editing) + { + @L["Unknown or empty (user comments)"] + } + @if (!_editing || + !_unknownComments) + { + + + + @L["Please enter valid comments."] + + + + } + + } +
+ + + +
+ @if (!_editing) + { + + } + else + { + + + } + @L["Back to machine"] +
diff --git a/Marechai/Pages/Admin/Details/MachinePhoto.razor.cs b/Marechai/Pages/Admin/Details/MachinePhoto.razor.cs new file mode 100644 index 00000000..038f62c9 --- /dev/null +++ b/Marechai/Pages/Admin/Details/MachinePhoto.razor.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Blazorise; +using Marechai.Database; +using Marechai.Database.Models; +using Marechai.Shared; +using Marechai.ViewModels; +using Microsoft.AspNetCore.Components; +using Orientation = Marechai.Database.Orientation; + +namespace Marechai.Pages.Admin.Details +{ + public partial class MachinePhoto + { + const int _maxUploadSize = 25 * 1048576; + bool _editing; + List _licenses; + bool _loaded; + MachinePhotoViewModel _model; + bool _unknownAperture; + bool _unknownAuthor; + bool _unknownCameraManufacturer; + bool _unknownCameraModel; + bool _unknownColorSpace; + bool _unknownComments; + bool _unknownContrast; + bool _unknownCreationDate; + bool _unknownDigitalZoomRatio; + bool _unknownExifVersion; + bool _unknownExposure; + bool _unknownExposureMethod; + bool _unknownExposureProgram; + bool _unknownFlash; + bool _unknownFocal; + bool _unknownFocalLength; + bool _unknownFocalLengthEquivalent; + bool _unknownHorizontalResolution; + bool _unknownIsoRating; + bool _unknownLens; + bool _unknownLightSource; + bool _unknownMeteringMode; + bool _unknownOrientation; + bool _unknownResolutionUnit; + bool _unknownSaturation; + bool _unknownSceneCaptureType; + bool _unknownSensingMethod; + bool _unknownSharpness; + bool _unknownSoftwareUsed; + bool _unknownSource; + bool _unknownSubjectDistanceRange; + bool _unknownVerticalResolution; + bool _unknownWhiteBalance; + ApplicationUser _user; + [Parameter] + public Guid Id { get; set; } + [Parameter] + public int MachineId { get; set; } + + ushort ColorSpace + { + get + { + if(_model.ColorSpace is null) + return 0; + + return(ushort)_model.ColorSpace; + } + set => _model.ColorSpace = (ColorSpace)value; + } + + ushort Contrast + { + get + { + if(_model.Contrast is null) + return 0; + + return(ushort)_model.Contrast; + } + set => _model.Contrast = (Contrast)value; + } + + ushort ExposureMode + { + get + { + if(_model.ExposureMethod is null) + return 0; + + return(ushort)_model.ExposureMethod; + } + set => _model.ExposureMethod = (ExposureMode)value; + } + + ushort ExposureProgram + { + get + { + if(_model.ExposureProgram is null) + return 0; + + return(ushort)_model.ExposureProgram; + } + set => _model.ExposureProgram = (ExposureProgram)value; + } + + ushort Flash + { + get + { + if(_model.Flash is null) + return 0; + + return(ushort)_model.Flash; + } + set => _model.Flash = (Flash)value; + } + + ushort LightSource + { + get + { + if(_model.LightSource is null) + return 0; + + return(ushort)_model.LightSource; + } + set => _model.LightSource = (LightSource)value; + } + + ushort MeteringMode + { + get + { + if(_model.MeteringMode is null) + return 0; + + return(ushort)_model.MeteringMode; + } + set => _model.MeteringMode = (MeteringMode)value; + } + + ushort ResolutionUnit + { + get + { + if(_model.ResolutionUnit is null) + return 0; + + return(ushort)_model.ResolutionUnit; + } + set => _model.ResolutionUnit = (ResolutionUnit)value; + } + + ushort Orientation + { + get + { + if(_model.Orientation is null) + return 0; + + return(ushort)_model.Orientation; + } + set => _model.Orientation = (Orientation)value; + } + + ushort Saturation + { + get + { + if(_model.Saturation is null) + return 0; + + return(ushort)_model.Saturation; + } + set => _model.Saturation = (Saturation)value; + } + + ushort SceneCaptureType + { + get + { + if(_model.SceneCaptureType is null) + return 0; + + return(ushort)_model.SceneCaptureType; + } + set => _model.SceneCaptureType = (SceneCaptureType)value; + } + + ushort SensingMethod + { + get + { + if(_model.SensingMethod is null) + return 0; + + return(ushort)_model.SensingMethod; + } + set => _model.SensingMethod = (SensingMethod)value; + } + + ushort Sharpness + { + get + { + if(_model.Sharpness is null) + return 0; + + return(ushort)_model.Sharpness; + } + set => _model.Sharpness = (Sharpness)value; + } + + ushort SubjectDistanceRange + { + get + { + if(_model.SubjectDistanceRange is null) + return 0; + + return(ushort)_model.SubjectDistanceRange; + } + set => _model.SubjectDistanceRange = (SubjectDistanceRange)value; + } + + ushort WhiteBalance + { + get + { + if(_model.WhiteBalance is null) + return 0; + + return(ushort)_model.WhiteBalance; + } + set => _model.WhiteBalance = (WhiteBalance)value; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(_loaded) + return; + + _loaded = true; + + if(Id == Guid.Empty) + return; + + _model = await Service.GetAsync(Id); + _licenses = await LicensesService.GetAsync(); + + _editing = NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLowerInvariant(). + StartsWith("admin/machines/photo/edit/", StringComparison.InvariantCulture); + + if(_editing) + SetCheckboxes(); + + _user = await UserManager.FindByIdAsync(_model.UserId); + + StateHasChanged(); + } + + void SetCheckboxes() + { + _unknownAperture = _model.Aperture is null; + _unknownAuthor = _model.Author is null; + _unknownSource = _model.Source is null; + _unknownCameraManufacturer = _model.CameraManufacturer is null; + _unknownCameraModel = _model.CameraModel is null; + _unknownColorSpace = _model.ColorSpace is null; + _unknownContrast = _model.Contrast is null; + _unknownCreationDate = _model.CreationDate is null; + _unknownDigitalZoomRatio = _model.DigitalZoomRatio is null; + _unknownExifVersion = _model.ExifVersion is null; + _unknownExposure = _model.ExposureTime is null; + _unknownExposureMethod = _model.ExposureMethod is null; + _unknownExposureProgram = _model.ExposureProgram is null; + _unknownFlash = _model.Flash is null; + _unknownFocal = _model.Focal is null; + _unknownFocalLength = _model.FocalLength is null; + _unknownFocalLengthEquivalent = _model.FocalLengthEquivalent is null; + _unknownIsoRating = _model.IsoRating is null; + _unknownLens = _model.Lens is null; + _unknownLightSource = _model.LightSource is null; + _unknownMeteringMode = _model.MeteringMode is null; + _unknownResolutionUnit = _model.ResolutionUnit is null; + _unknownOrientation = _model.Orientation is null; + _unknownSaturation = _model.Saturation is null; + _unknownSceneCaptureType = _model.SceneCaptureType is null; + _unknownSensingMethod = _model.SensingMethod is null; + _unknownSharpness = _model.Sharpness is null; + _unknownSoftwareUsed = _model.SoftwareUsed is null; + _unknownSubjectDistanceRange = _model.SoftwareUsed is null; + _unknownHorizontalResolution = _model.HorizontalResolution is null; + _unknownVerticalResolution = _model.VerticalResolution is null; + _unknownWhiteBalance = _model.WhiteBalance is null; + _unknownComments = _model.Comments is null; + } + + void OnEditClicked() + { + _editing = true; + SetCheckboxes(); + StateHasChanged(); + } + + async void OnCancelClicked() + { + _editing = false; + + _model = await Service.GetAsync(Id); + SetCheckboxes(); + StateHasChanged(); + } + + async void OnSaveClicked() + { + await Service.UpdateAsync(_model); + _editing = false; + _model = await Service.GetAsync(Id); + SetCheckboxes(); + StateHasChanged(); + } + + void ValidateDoubleBiggerThanZero(ValidatorEventArgs e) + { + if(e.Value is double item && + item > 0) + e.Status = ValidationStatus.Success; + else + e.Status = ValidationStatus.Error; + } + + void ValidateDoubleBiggerOrEqualThanZero(ValidatorEventArgs e) + { + if(e.Value is double item && + item >= 0) + e.Status = ValidationStatus.Success; + else + e.Status = ValidationStatus.Error; + } + + void ValidateUnsignedShortBiggerThanZero(ValidatorEventArgs e) + { + if(e.Value is ushort item && + item > 0) + e.Status = ValidationStatus.Success; + else + e.Status = ValidationStatus.Error; + } + + void ValidateAuthor(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Author must be 255 characters or less."], 255); + + void ValidateSource(ValidatorEventArgs e) => + Validators.ValidateUrl(e, L["Source URL must be smaller than 255 characters."], 255); + + void ValidateCameraManufacturer(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Camera manufacturer must be 255 characters or less."], 255); + + void ValidateCameraModel(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Camera model must be 255 characters or less."], 255); + + void ValidateDate(ValidatorEventArgs e) + { + if(!(e.Value is DateTime item) || + item.Year <= 1816 || + item >= DateTime.UtcNow) + e.Status = ValidationStatus.Error; + else + e.Status = ValidationStatus.Success; + } + + void ValidateExifVersion(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Exif version must be 255 characters or less."], 255); + + void ValidateLens(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Lens name must be 255 characters or less."], 255); + + void ValidateSoftwareUsed(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Software used must be 255 characters or less."], 255); + + void ValidateComments(ValidatorEventArgs e) => + Validators.ValidateString(e, L["User comments must be 255 characters or less."], 255); + } +} \ No newline at end of file diff --git a/Marechai/Pages/Computers/Search.razor b/Marechai/Pages/Computers/Search.razor index e31cbe20..89928811 100644 --- a/Marechai/Pages/Computers/Search.razor +++ b/Marechai/Pages/Computers/Search.razor @@ -62,7 +62,7 @@ @foreach (var computer in _computers) { - @computer.CompanyName @computer.Name + @computer.Company @computer.Name
}

diff --git a/Marechai/Pages/Consoles/Search.razor b/Marechai/Pages/Consoles/Search.razor index 460d53db..549002bb 100644 --- a/Marechai/Pages/Consoles/Search.razor +++ b/Marechai/Pages/Consoles/Search.razor @@ -62,7 +62,7 @@ @foreach (var console in _consoles) { - @console.CompanyName @console.Name + @console.Company @console.Name
}

diff --git a/Marechai/Pages/Machines/View.razor b/Marechai/Pages/Machines/View.razor index b82f6c3c..c5cf6fea 100644 --- a/Marechai/Pages/Machines/View.razor +++ b/Marechai/Pages/Machines/View.razor @@ -77,7 +77,7 @@ } - @_machine.CompanyName @_machine.Name + @_machine.Company @_machine.Name @if (_machine.Introduced.HasValue && diff --git a/Marechai/Program.cs b/Marechai/Program.cs index a3c4c953..998e0fd1 100644 --- a/Marechai/Program.cs +++ b/Marechai/Program.cs @@ -174,21 +174,8 @@ namespace Marechai (end - start).TotalSeconds); start = DateTime.Now; - Console.WriteLine("\u001b[31;1mImporting photos...\u001b[0m"); - - try - { - Photos.ImportPhotos(context); - } - catch(Exception e) - { - Console.WriteLine("Exception {0} importing photos, saving changes and continuing...", e); - - throw; - } - - context.SaveChanges(); - + Console.WriteLine("\u001b[31;1mEnsuring photo folders exist...\u001b[0m"); + Photos.EnsureCreated("wwwroot"); end = DateTime.Now; Console.WriteLine("\u001b[31;1mTook \u001b[32;1m{0} seconds\u001b[31;1m...\u001b[0m", diff --git a/Marechai/Resources/Services/MachinePhotosService.en.resx b/Marechai/Resources/Services/MachinePhotosService.en.resx new file mode 100644 index 00000000..a17a4701 --- /dev/null +++ b/Marechai/Resources/Services/MachinePhotosService.en.resx @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unknown + Unknown, referring to a photo's source URL + + + Unknown + Unknown, referring to a photo author + + + Unknown + Unknown, referring to a photo aperture + + + Unknown + Unknown, referring to a camera manufacturer + + + Unknown + Unknown, referring to a camera model + + + Unknown + Unknown, referring to a photo color space + + + Unknown + Unknown, referring to a photo contrast + + + Unknown + Unknown, referring to a photo creation date + + + Unknown + Unknown, referring to a photo digital zoom ratio + + + Unknown + Unknown, referring to a photo exif version + + + Unknown + Unknown, referring to a photo exposure time + + + Unknown + Unknown, referring to a photo exposure mode + + + Unknown + Unknown, referring to a photo exposure program + + + Unknown + Unknown, referring to a photo flash + + + Unknown + Unknown, referring to a focal number + + + Unknown + Unknown, referring to a focal length + + + Unknown + Unknown, referring to a focal length + + + Unknown + Unknown, referring to a resolution + + + Unknown + Unknown, referring to an ISO rating + + + Unknown + Unknown, referring to a lens model or name + + + Unknown + Unknown, referring to a light source + + + Unknown + Unknown, referring to a metering mode + + + Unknown + Unknown, referring to a resolution unit + + + Unknown + Unknown, referring to a orientation + + + Unknown + Unknown, referring to a saturation + + + Unknown + Unknown, referring to a scene capture type + + + Unknown + Unknown, referring to a sensing method + + + Unknown + Unknown, referring to a sharpness + + + Unknown + Unknown, referring to a software used + + + Unknown + Unknown, referring to a subject distance range + + + Unknown + Unknown, referring to a vertical resolution + + + Unknown + Unknown, referring to a white balance + + + Unknown + Unknown, referring to user comments + + \ No newline at end of file diff --git a/Marechai/Resources/Services/MachinePhotosService.es.resx b/Marechai/Resources/Services/MachinePhotosService.es.resx new file mode 100644 index 00000000..94804f1d --- /dev/null +++ b/Marechai/Resources/Services/MachinePhotosService.es.resx @@ -0,0 +1,709 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Descconocida + Unknown, referring to a photo's source URL + + + Desconocido + Unknown, referring to a photo author + + + Desconocida + Unknown, referring to a photo aperture + + + Desconocido + Unknown, referring to a camera manufacturer + + + Desconocido + Unknown, referring to a camera model + + + Desconocido + Unknown, referring to a photo color space + + + Desconocido + Unknown, referring to a photo contrast + + + Desconocida + Unknown, referring to a photo creation date + + + Desconocida + Unknown, referring to a photo digital zoom ratio + + + Desconocida + Unknown, referring to a photo exif version + + + Desconocido + Unknown, referring to a photo exposure time + + + Desconocido + Unknown, referring to a photo exposure mode + + + Desconocido + Unknown, referring to a photo exposure program + + + Desconocido + Unknown, referring to a photo flash + + + Desconocido + Unknown, referring to a focal number + + + Desconocida + Unknown, referring to a focal length + + + Desconocida + Unknown, referring to a focal length + + + Desconocida + Unknown, referring to a resolution + + + Desconocida + Unknown, referring to an ISO rating + + + Desconocido + Unknown, referring to a lens model or name + + + Desconocida + Unknown, referring to a light source + + + Desconocido + Unknown, referring to a metering mode + + + Desconocida + Unknown, referring to a resolution unit + + + Desconocida + Unknown, referring to a orientation + + + Desconocida + Unknown, referring to a saturation + + + Desconocido + Unknown, referring to a scene capture type + + + Desconocido + Unknown, referring to a sensing method + + + Desconocida + Unknown, referring to a sharpness + + + Desconocido + Unknown, referring to a software used + + + Desconocido + Unknown, referring to a subject distance range + + + Desconocida + Unknown, referring to a vertical resolution + + + Desconocido + Unknown, referring to a white balance + + + Desconocidos o ninguno + Unknown, referring to user comments + + + Cargando... + Loading... + + + Subir foto para la máquina {0} fabricada por {1} + Upload photo for machine {0} manufactured by {1} + + + Elige archivo de foto + Choose photo file + + + Licencia + License + + + Dirección de origen + Source URL + + + Por favor introduce una dirección de origen válida. + Please enter a valid source URL. + + + Subir + Upload + + + Formato de imagen reconocido como {0} + Image format recognized as {0} + + + ¡Terminado! + All finished! + + + Ir a detalles de la foto + Go to photo details + + + Extrayendo información Exif... + Extracting Exif information... + + + ¡OK! + OK! + + + ¡Error! + Error! + + + Moviendo el archivo al lugar adecuado... + Moving file to proper place... + + + Convirtiendo foto a JPEG con una resolución HD (miniatura)... + Converting photo to JPEG with an HD resolution (thumbnail)... + + + Convirtiendo foto a JPEG con una resolución 1440p (miniatura)... + Converting photo to JPEG with a 1440p resolution (thumbnail)... + + + Convirtiendo foto a JPEG con una resolución 4K (miniatura)... + Converting photo to JPEG with a 4K resolution (thumbnail)... + + + Convirtiendo foto a JPEG con una resolución HD... + Converting photo to JPEG with an HD resolution... + + + Convirtiendo foto a JPEG con una resolución 1440p... + Converting photo to JPEG with a 1440p resolution... + + + Convirtiendo foto a JPEG con una resolución 4K... + Converting photo to JPEG with a 4K resolution... + + + Convirtiendo foto a JPEG2000 con una resolución HD (miniatura)... + Converting photo to JPEG2000 with an HD resolution (thumbnail)... + + + Convirtiendo foto a JPEG2000 con una resolución 1440p (miniatura)... + Converting photo to JPEG2000 with a 1440p resolution (thumbnail)... + + + Convirtiendo foto a JPEG2000 con una resolución 4K (miniatura)... + Converting photo to JPEG2000 with a 4K resolution (thumbnail)... + + + Convirtiendo foto a JPEG2000 con una resolución HD... + Converting photo to JPEG2000 with an HD resolution... + + + AAAConvirtiendo foto a JPEG2000 con una resolución 1440p... + Converting photo to JPEG2000 with a 1440p resolution... + + + Convirtiendo foto a JPEG2000 con una resolución 4K... + Converting photo to JPEG2000 with a 4K resolution... + + + Convirtiendo foto a WebP con una resolución HD (miniatura)... + Converting photo to WebP with an HD resolution (thumbnail)... + + + Convirtiendo foto a WebP con una resolución 1440p (miniatura)... + Converting photo to WebP with a 1440p resolution (thumbnail)... + + + Convirtiendo foto a WebP con una resolución 4K (miniatura)... + Converting photo to WebP with a 4K resolution (thumbnail)... + + + Convirtiendo foto a WebP con una resolución HD... + Converting photo to WebP with an HD resolution... + + + Convirtiendo foto a WebP con una resolución 1440p... + Converting photo to WebP with a 1440p resolution... + + + Convirtiendo foto a WebP con una resolución 4K... + Converting photo to WebP with a 4K resolution... + + + Convirtiendo foto a HEIF con una resolución HD (miniatura)... + Converting photo to HEIF with an HD resolution (thumbnail)... + + + Convirtiendo foto a HEIF con una resolución 1440p (miniatura)... + Converting photo to HEIF with a 1440p resolution (thumbnail)... + + + Convirtiendo foto a HEIF con una resolución 4K (miniatura)... + Converting photo to HEIF with a 4K resolution (thumbnail)... + + + Convirtiendo foto a HEIF con una resolución HD... + Converting photo to HEIF with an HD resolution... + + + Convirtiendo foto a HEIF con una resolución 1440p... + Converting photo to HEIF with a 1440p resolution... + + + Convirtiendo foto a HEIF con una resolución 4K... + Converting photo to HEIF with a 4K resolution... + + + Convirtiendo foto a AV1F con una resolución HD (miniatura)... + Converting photo to AV1F with an HD resolution (thumbnail)... + + + Convirtiendo foto a AV1F con una resolución 1440p (miniatura)... + Converting photo to AV1F with a 1440p resolution (thumbnail)... + + + Convirtiendo foto a AV1F con una resolución 4K (miniatura)... + Converting photo to AV1F with a 4K resolution (thumbnail)... + + + Convirtiendo foto a AV1F con una resolución HD... + Converting photo to AV1F with an HD resolution... + + + Convirtiendo foto a AV1F con una resolución 1440p... + Converting photo to AV1F with a 1440p resolution... + + + Convirtiendo foto a AV1F con una resolución 4K... + Converting photo to AV1F with a 4K resolution... + + + Añadiendo foto a la base de datos... + Adding photo to database... + + + Por favor elige una licencia válida. + Please choose a valid license. + + + No se puede ejecutar ImageMagick, por favor contacta con el administrador. + Cannot run ImageMagick please contact the administrator. + + + El archivo seleccionado es demasiado grande. + The selected file is too big. + + + Ocurrió un error subiendo el archivo. + There was an error uploading the file. + + + El archivo seleccionado no se ha reconocido como una imagen. + The uploaded file was not recognized as an image. + + + La dirección de origen debe contener menos de 255 caracteres. + Source URL must be smaller than 255 characters. + + + Detalles de foto de máquina + Machine photo details + + + Máquina + Machine + + + Subida por + Uploaded by + + + Subida el + Uploaded date + + + Autor + Author + + + Apertura + Aperture + + + Por favor introduce una apertura válida. + Please enter a valid aperture. + + + Fabricante de la cámara + Camera manufacturer + + + Por favor introduce un fabricante válido. + Please enter a valid camera manufacturer. + + + Modelo de la cámara + Camera model + + + Por favor introduce un modelo válido. + Please enter a valid camera model. + + + Espacio de color + Color space + + + Contraste + Contrast + + + Fecha de creación + Creation date + + + Por favor introduce una fecha de creación válida. + Please enter a correct creation date. + + + Relación del zoom digital + Digital zoom ratio + + + Por favor introduce una relación válida. + Please enter a valid digital zoom ratio. + + + Versión Exif + Exif version + + + Por favor introduce una versión Exif válida. + Please enter a valid Exif version. + + + Tiempo de exposición + Exposure time + + + Por favor introduce un tiempo de exposición válido. + Please enter a valid exposure time. + + + Modo de exposición + Exposure mode + + + Programa de exposición + Exposure program + + + Flash + Flash + + + Focal + F-number + + + Por favor introduce un número focal válido. + Please enter a valid focal number. + + + Longitud focal + Focal length + + + Por favor introduce una longitud focal válida. + Please enter a valid focal length. + + + Longitud focal en película de 35mm + Focal length in 35mm film + + + Por favor introduce una longitud focal válida. + Please enter a valid focal length in 35mm film. + + + Resolución horizontal + Horizontal resolution + + + Por favor introduce una resolución válida. + Please enter a valid horizontal resolution. + + + Sensibilidad ISO + ISO rating + + + Por favor introduce una sensibilidad válida. + Please enter a valid ISO rating. + + + Objectivo + Lens + + + Por favor introduce un objetivo válido. + Please enter a valid lens. + + + Fuente de luz + Light source + + + Modo de medición + Metering mode + + + Unidad de resolución + Resolution unit + + + Orientación + Orientation + + + Saturación + Saturation + + + Tipo de captura de escena + Scene capture type + + + Método de sensibilidad + Sensing method + + + Nitidez + Sharpness + + + Software utilizado + Software used + + + Por favor introduce un software utilizado válido. + Please enter a valid software used. + + + Rango de distancia del sujeto + Subject distance range + + + Resolución vertical + Vertical resolution + + + Por favor introduce una resolución válida. + Please enter a valid vertical resolution. + + + Balance de blancos + White balance + + + Comentarios de usuario + User comments + + + Por favor introduce unos comentarios válidos. + Please enter valid comments. + + + Editar + Edit + + + Guardar + Save + + + Cancelar + Cancel + + + Volver a la máquina + Back to machine + + + El autor debe contener 255 caracteres o menos. + Author must be 255 characters or less. + + + El fabricante de la cámara debe contener 255 caracteres o menos. + Camera manufacturer must be 255 characters or less. + + + El modelo de la cámara debe contener 255 caracteres o menos. + Camera model must be 255 characters or less. + + + La versión Exif debe contener 255 caracteres o menos. + Exif version must be 255 characters or less. + + + El nombre del objetivo debe contener 255 caracteres o menos. + Lens name must be 255 characters or less. + + + El software utilizado debe contener 255 caracteres o menos. + Software used must be 255 characters or less. + + + Los comentarios deben contener 255 caracteres o menos. + User comments must be 255 characters or less. + + \ No newline at end of file diff --git a/Marechai/Services/ComputersService.cs b/Marechai/Services/ComputersService.cs index 374c8504..2f8aba8a 100644 --- a/Marechai/Services/ComputersService.cs +++ b/Marechai/Services/ComputersService.cs @@ -35,20 +35,14 @@ using Marechai.Database; using Marechai.Database.Models; using Marechai.ViewModels; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Localization; namespace Marechai.Services { public class ComputersService { - readonly MarechaiContext _context; - readonly IStringLocalizer _l; + readonly MarechaiContext _context; - public ComputersService(MarechaiContext context, IStringLocalizer localizer) - { - _context = context; - _l = localizer; - } + public ComputersService(MarechaiContext context) => _context = context; public async Task GetComputersCountAsync() => await _context.Machines.CountAsync(c => c.Type == MachineType.Computer); @@ -70,7 +64,7 @@ namespace Marechai.Services Where(m => m.Type == MachineType.Computer && EF.Functions.Like(m.Name, $"{c}%")). OrderBy(m => m.Company.Name).ThenBy(m => m.Name).Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); public async Task> GetComputersByYearAsync(int year) => @@ -79,14 +73,14 @@ namespace Marechai.Services m.Introduced.Value.Year == year).OrderBy(m => m.Company.Name).ThenBy(m => m.Name). Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); public async Task> GetComputersAsync() => await _context.Machines.Include(m => m.Company).Where(m => m.Type == MachineType.Computer). OrderBy(m => m.Company.Name).ThenBy(m => m.Name).Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); } } \ No newline at end of file diff --git a/Marechai/Services/ConsolesService.cs b/Marechai/Services/ConsolesService.cs index 9c07f478..7b02f002 100644 --- a/Marechai/Services/ConsolesService.cs +++ b/Marechai/Services/ConsolesService.cs @@ -35,20 +35,14 @@ using Marechai.Database; using Marechai.Database.Models; using Marechai.ViewModels; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Localization; namespace Marechai.Services { public class ConsolesService { - readonly MarechaiContext _context; - readonly IStringLocalizer _l; + readonly MarechaiContext _context; - public ConsolesService(MarechaiContext context, IStringLocalizer localizer) - { - _context = context; - _l = localizer; - } + public ConsolesService(MarechaiContext context) => _context = context; public async Task GetConsolesCountAsync() => await _context.Machines.CountAsync(c => c.Type == MachineType.Console); @@ -70,7 +64,7 @@ namespace Marechai.Services Where(m => m.Type == MachineType.Console && EF.Functions.Like(m.Name, $"{c}%")). OrderBy(m => m.Company.Name).ThenBy(m => m.Name).Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); public async Task> GetConsolesByYearAsync(int year) => @@ -79,14 +73,14 @@ namespace Marechai.Services m.Introduced.Value.Year == year).OrderBy(m => m.Company.Name).ThenBy(m => m.Name). Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); public async Task> GetConsolesAsync() => await _context.Machines.Include(m => m.Company).Where(m => m.Type == MachineType.Console). OrderBy(m => m.Company.Name).ThenBy(m => m.Name).Select(m => new MachineViewModel { - Id = m.Id, Name = m.Name, CompanyName = m.Company.Name + Id = m.Id, Name = m.Name, Company = m.Company.Name }).ToListAsync(); } } \ No newline at end of file diff --git a/Marechai/Services/MachinePhotosService.cs b/Marechai/Services/MachinePhotosService.cs new file mode 100644 index 00000000..ac9bff1b --- /dev/null +++ b/Marechai/Services/MachinePhotosService.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Marechai.Database.Models; +using Marechai.ViewModels; +using Microsoft.EntityFrameworkCore; + +namespace Marechai.Services +{ + public class MachinePhotosService + { + readonly MarechaiContext _context; + + public MachinePhotosService(MarechaiContext context) => _context = context; + + public async Task> GetGuidsByMachineAsync(int machineId) => + await _context.MachinePhotos.Where(p => p.MachineId == machineId).Select(p => p.Id).ToListAsync(); + + // TODO: Get only the needed parts of ApplicationUser + public async Task GetAsync(Guid id) => + await _context.MachinePhotos.Where(p => p.Id == id).Select(p => new MachinePhotoViewModel + { + Aperture = p.Aperture, Author = p.Author, CameraManufacturer = p.CameraManufacturer, + CameraModel = p.CameraModel, ColorSpace = p.ColorSpace, Comments = p.Comments, Contrast = p.Contrast, + CreationDate = p.CreationDate, DigitalZoomRatio = p.DigitalZoomRatio, ExifVersion = p.ExifVersion, + ExposureTime = p.ExposureTime, ExposureMethod = p.ExposureMethod, ExposureProgram = p.ExposureProgram, + Flash = p.Flash, Focal = p.Focal, FocalLength = p.FocalLength, + FocalLengthEquivalent = p.FocalLengthEquivalent, HorizontalResolution = p.HorizontalResolution, + Id = p.Id, IsoRating = p.IsoRating, Lens = p.Lens, LicenseId = p.LicenseId, + LicenseName = p.License.Name, LightSource = p.LightSource, MachineCompanyName = p.Machine.Company.Name, + MachineId = p.MachineId, MachineName = p.Machine.Name, MeteringMode = p.MeteringMode, + ResolutionUnit = p.ResolutionUnit, Orientation = p.Orientation, Saturation = p.Saturation, + SceneCaptureType = p.SceneCaptureType, SensingMethod = p.SensingMethod, Sharpness = p.Sharpness, + SoftwareUsed = p.SoftwareUsed, Source = p.Source, SubjectDistanceRange = p.SubjectDistanceRange, + UploadDate = p.UploadDate, UserId = p.UserId, VerticalResolution = p.VerticalResolution, + WhiteBalance = p.WhiteBalance, OriginalExtension = p.OriginalExtension + }).FirstOrDefaultAsync(); + + public async Task UpdateAsync(MachinePhotoViewModel viewModel) + { + MachinePhoto model = await _context.MachinePhotos.FindAsync(viewModel.Id); + + if(model is null) + return; + + model.Aperture = viewModel.Aperture; + model.Author = viewModel.Author; + model.CameraManufacturer = viewModel.CameraManufacturer; + model.CameraModel = viewModel.CameraModel; + model.ColorSpace = viewModel.ColorSpace; + model.Comments = viewModel.Comments; + model.Contrast = viewModel.Contrast; + model.CreationDate = viewModel.CreationDate; + model.DigitalZoomRatio = viewModel.DigitalZoomRatio; + model.ExifVersion = viewModel.ExifVersion; + model.ExposureTime = viewModel.ExposureTime; + model.ExposureMethod = viewModel.ExposureMethod; + model.ExposureProgram = viewModel.ExposureProgram; + model.Flash = viewModel.Flash; + model.Focal = viewModel.Focal; + model.FocalLength = viewModel.FocalLength; + model.FocalLengthEquivalent = viewModel.FocalLengthEquivalent; + model.HorizontalResolution = viewModel.HorizontalResolution; + model.IsoRating = viewModel.IsoRating; + model.Lens = viewModel.Lens; + model.LicenseId = viewModel.LicenseId; + model.LightSource = viewModel.LightSource; + model.MeteringMode = viewModel.MeteringMode; + model.ResolutionUnit = viewModel.ResolutionUnit; + model.Orientation = viewModel.Orientation; + model.Saturation = viewModel.Saturation; + model.SceneCaptureType = viewModel.SceneCaptureType; + model.SensingMethod = viewModel.SensingMethod; + model.Sharpness = viewModel.Sharpness; + model.SoftwareUsed = viewModel.SoftwareUsed; + model.Source = viewModel.Source; + model.SubjectDistanceRange = viewModel.SubjectDistanceRange; + model.VerticalResolution = viewModel.VerticalResolution; + model.WhiteBalance = viewModel.WhiteBalance; + + await _context.SaveChangesAsync(); + } + + public async Task CreateAsync(MachinePhotoViewModel viewModel) + { + var model = new MachinePhoto + { + Aperture = viewModel.Aperture, Author = viewModel.Author, + CameraManufacturer = viewModel.CameraManufacturer, CameraModel = viewModel.CameraModel, + ColorSpace = viewModel.ColorSpace, Comments = viewModel.Comments, Contrast = viewModel.Contrast, + CreationDate = viewModel.CreationDate, DigitalZoomRatio = viewModel.DigitalZoomRatio, + ExifVersion = viewModel.ExifVersion, ExposureTime = viewModel.ExposureTime, + ExposureMethod = viewModel.ExposureMethod, ExposureProgram = viewModel.ExposureProgram, + Flash = viewModel.Flash, Focal = viewModel.Focal, FocalLength = viewModel.FocalLength, + FocalLengthEquivalent = viewModel.FocalLengthEquivalent, + HorizontalResolution = viewModel.HorizontalResolution, Id = viewModel.Id, + IsoRating = viewModel.IsoRating, Lens = viewModel.Lens, LicenseId = viewModel.LicenseId, + LightSource = viewModel.LightSource, MachineId = viewModel.MachineId, + MeteringMode = viewModel.MeteringMode, ResolutionUnit = viewModel.ResolutionUnit, + Orientation = viewModel.Orientation, Saturation = viewModel.Saturation, + SceneCaptureType = viewModel.SceneCaptureType, SensingMethod = viewModel.SensingMethod, + Sharpness = viewModel.Sharpness, SoftwareUsed = viewModel.SoftwareUsed, Source = viewModel.Source, + SubjectDistanceRange = viewModel.SubjectDistanceRange, UploadDate = viewModel.UploadDate, + UserId = viewModel.UserId, VerticalResolution = viewModel.VerticalResolution, + WhiteBalance = viewModel.WhiteBalance, OriginalExtension = viewModel.OriginalExtension + }; + + await _context.MachinePhotos.AddAsync(model); + await _context.SaveChangesAsync(); + + return model.Id; + } + } +} \ No newline at end of file diff --git a/Marechai/Services/MachinesService.cs b/Marechai/Services/MachinesService.cs index d371ca2c..6a6c8eae 100644 --- a/Marechai/Services/MachinesService.cs +++ b/Marechai/Services/MachinesService.cs @@ -38,7 +38,8 @@ namespace Marechai.Services public async Task GetAsync(int id) => await _context.Machines.Where(m => m.Id == id). Select(m => new MachineViewModel { - Id = m.Id, CompanyId = m.CompanyId, + Id = m.Id, Company = m.Company.Name, + CompanyId = m.CompanyId, Name = m.Name, Model = m.Model, Introduced = m.Introduced, Type = m.Type, FamilyId = m.FamilyId @@ -92,7 +93,7 @@ namespace Marechai.Services if(company != null) { - model.CompanyName = company.Name; + model.Company = company.Name; IQueryable logos = _context.CompanyLogos.Where(l => l.CompanyId == company.Id); diff --git a/Marechai/Services/Register.cs b/Marechai/Services/Register.cs index 3f21a2b5..864870d3 100644 --- a/Marechai/Services/Register.cs +++ b/Marechai/Services/Register.cs @@ -71,6 +71,7 @@ namespace Marechai.Services services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } } } \ No newline at end of file diff --git a/Marechai/ViewModels/BasePhotoViewModel.cs b/Marechai/ViewModels/BasePhotoViewModel.cs new file mode 100644 index 00000000..39baad40 --- /dev/null +++ b/Marechai/ViewModels/BasePhotoViewModel.cs @@ -0,0 +1,46 @@ +using System; +using Marechai.Database; + +namespace Marechai.ViewModels +{ + public class BasePhotoViewModel : BaseViewModel + { + public double? Aperture { get; set; } + public string Author { get; set; } + public string CameraManufacturer { get; set; } + public string CameraModel { get; set; } + public ColorSpace? ColorSpace { get; set; } + public string Comments { get; set; } + public Contrast? Contrast { get; set; } + public DateTime? CreationDate { get; set; } + public double? DigitalZoomRatio { get; set; } + public string ExifVersion { get; set; } + public double? ExposureTime { get; set; } + public ExposureMode? ExposureMethod { get; set; } + public ExposureProgram? ExposureProgram { get; set; } + public Flash? Flash { get; set; } + public double? Focal { get; set; } + public double? FocalLength { get; set; } + public double? FocalLengthEquivalent { get; set; } + public double? HorizontalResolution { get; set; } + public ushort? IsoRating { get; set; } + public string Lens { get; set; } + public LightSource? LightSource { get; set; } + public MeteringMode? MeteringMode { get; set; } + public ResolutionUnit? ResolutionUnit { get; set; } + public Orientation? Orientation { get; set; } + public Saturation? Saturation { get; set; } + public SceneCaptureType? SceneCaptureType { get; set; } + public SensingMethod? SensingMethod { get; set; } + public Sharpness? Sharpness { get; set; } + public string SoftwareUsed { get; set; } + public SubjectDistanceRange? SubjectDistanceRange { get; set; } + public DateTime UploadDate { get; set; } + public double? VerticalResolution { get; set; } + public WhiteBalance? WhiteBalance { get; set; } + public string UserId { get; set; } + public string LicenseName { get; set; } + public int LicenseId { get; set; } + public string OriginalExtension { get; set; } + } +} \ No newline at end of file diff --git a/Marechai/ViewModels/MachinePhotoViewModel.cs b/Marechai/ViewModels/MachinePhotoViewModel.cs new file mode 100644 index 00000000..c2c6204e --- /dev/null +++ b/Marechai/ViewModels/MachinePhotoViewModel.cs @@ -0,0 +1,10 @@ +namespace Marechai.ViewModels +{ + public class MachinePhotoViewModel : BasePhotoViewModel + { + public string Source { get; set; } + public string MachineName { get; set; } + public string MachineCompanyName { get; set; } + public int MachineId { get; set; } + } +} \ No newline at end of file diff --git a/Marechai/ViewModels/MachineViewModel.cs b/Marechai/ViewModels/MachineViewModel.cs index 54c6db86..296b183d 100644 --- a/Marechai/ViewModels/MachineViewModel.cs +++ b/Marechai/ViewModels/MachineViewModel.cs @@ -8,7 +8,6 @@ namespace Marechai.ViewModels { public string Name { get; set; } public string Model { get; set; } - public string CompanyName { get; set; } public int CompanyId { get; set; } public Guid? CompanyLogo { get; set; } public DateTime? Introduced { get; set; }