Add code to upload machine photos as well as edit or view details about them.

This commit is contained in:
2020-05-31 03:43:56 +01:00
parent 556c5b2062
commit 0310fe1b31
32 changed files with 4388 additions and 1567 deletions

View File

@@ -1,5 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=avif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=claunia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Exiftool/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=flif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=heif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=kibibytes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Marechai/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Portillo/@EntryIndexedValue">True</s:Boolean>

View File

@@ -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<ApplicationUser> userManager;
public MachinePhotosController(MarechaiContext context, IWebHostEnvironment hostingEnvironment,
UserManager<ApplicationUser> userManager)
{
_context = context;
this.hostingEnvironment = hostingEnvironment;
this.userManager = userManager;
}
// GET: MachinePhotos
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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);
}
}

View File

@@ -1,87 +0,0 @@
@using Marechai.Database.Models
@using Microsoft.AspNetCore.Identity
@model Marechai.Areas.Admin.Models.MachinePhotoViewModel
@inject UserManager<ApplicationUser> UserManager
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Machine photo</h4>
<hr />
<div class="row">
<div class="col-md-8">
<p>
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.
</p>
<p>
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).
</p>
<p>
Photos that are not clear enough or of too small resolution will be removed of the database.
</p>
<p>
@* TODO Detect roles *@
This photo will not appear publicly until approved to an administrator.
</p>
<p>
Upload of inappropriate or unrelated photos will create a warning in your account. Several warnings mean your account would be terminated.
</p>
<p>
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.
</p>
</div>
</div>
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger">
</div>
<div class="form-group">
<label asp-for="Machine" class="control-label">
</label>
<select asp-for="MachineId" class="form-control" asp-items="ViewBag.MachineId">
</select>
</div>
<div class="form-group">
<label asp-for="License" class="control-label">
</label>
<select asp-for="LicenseId" class="form-control" asp-items="ViewBag.LicenseId">
</select>
</div>
<div class="form-group">
<label asp-for="UploadUser" class="control-label">
</label>
<input asp-for="UploadUser" class="form-control" value="@UserManager.GetUserName(User)" readonly />
<span asp-validation-for="UploadUser" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Source" class="control-label">
</label>
<input asp-for="Source" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Photo" class="control-label">
</label>
<input asp-for="Photo" class="form-control" />
<span asp-validation-for="Photo" class="text-danger">
</span>
@if (Model?.ErrorMessage != null)
{
<span class="text-danger">@Model.ErrorMessage</span>
}
</div>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Create" />
<a asp-action="Index" class="btn btn-secondary">
Back to List
</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@@ -1,53 +0,0 @@
@model Marechai.Areas.Admin.Models.MachinePhotoViewModel
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Machine photo</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Author)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Author)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.License)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.License)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Machine)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Machine)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.UploadDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.UploadDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.UploadUser)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.UploadUser)
</dd>
<dd class="col-sm-10">
<img src="/assets/photos/machines/@(Model.Id).jpg" />
</dd>
</dl>
<form asp-action="Delete">
<input type="hidden" asp-for="Id" />
<input class="btn btn-danger" type="submit" value="Delete" />
<a asp-action="Index" class="btn btn-secondary">
Back to List
</a>
</form>
</div>

View File

@@ -1,340 +0,0 @@
@model Marechai.Areas.Admin.Models.MachinePhotoDetailsViewModel
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Machine photo</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.UploadUser)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.UploadUser)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.UploadDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.UploadDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.License)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.License)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Machine)
</dt>
<dd class="col-sm-10">
<a asp-area="" asp-controller="Machine" asp-route-id="@Model.MachineId" asp-action="View" target="_blank">
@Html.DisplayFor(model => model.Machine)
</a>
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Source)
</dt>
<dd class="col-sm-10">
@if (Model.Source != null)
{
<a hred="@Html.DisplayFor(model => model.Source)">Link</a>
}
</dd>
<dd class="col-sm-10">
<img src="/assets/photos/machines/@(Model.Id).jpg" />
</dd>
</dl>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-primary">
Edit
</a>
<a asp-action="Index" class="btn btn-secondary">
Back to List
</a>
</div>
<dl class="row">
@if (Model.Author != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Author)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Author)
</dd>
}
@if (Model.CameraManufacturer != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.CameraManufacturer)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.CameraManufacturer)
</dd>
}
@if (Model.CameraModel != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.CameraModel)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.CameraModel)
</dd>
}
@if (Model.ColorSpace != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ColorSpace)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ColorSpace)
</dd>
}
@if (Model.Comments != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Comments)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Comments)
</dd>
}
@if (Model.Contrast != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Contrast)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Contrast)
</dd>
}
@if (Model.CreationDate != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.CreationDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.CreationDate)
</dd>
}
@if (Model.DigitalZoomRatio != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.DigitalZoomRatio)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.DigitalZoomRatio)
</dd>
}
@if (Model.ExifVersion != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ExifVersion)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ExifVersion)
</dd>
}
@if (Model.Exposure != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Exposure)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Exposure)
</dd>
}
@if (Model.ExposureMethod != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ExposureMethod)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ExposureMethod)
</dd>
}
@if (Model.ExposureProgram != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ExposureProgram)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ExposureProgram)
</dd>
}
@if (Model.Flash != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Flash)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Flash)
</dd>
}
@if (Model.Focal != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Focal)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Focal)
</dd>
}
@if (Model.FocalLength != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.FocalLength)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.FocalLength)
</dd>
}
@if (Model.FocalLengthEquivalent != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.FocalLengthEquivalent)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.FocalLengthEquivalent)
</dd>
}
@if (Model.HorizontalResolution != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.HorizontalResolution)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.HorizontalResolution)
</dd>
}
@if (Model.IsoRating != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.IsoRating)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.IsoRating)
</dd>
}
@if (Model.Lens != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Lens)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Lens)
</dd>
}
@if (Model.LightSource != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.LightSource)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.LightSource)
</dd>
}
@if (Model.MeteringMode != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.MeteringMode)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.MeteringMode)
</dd>
}
@if (Model.ResolutionUnit != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ResolutionUnit)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ResolutionUnit)
</dd>
}
@if (Model.Orientation != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Orientation)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Orientation)
</dd>
}
@if (Model.Saturation != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Saturation)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Saturation)
</dd>
}
@if (Model.SceneCaptureType != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.SceneCaptureType)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.SceneCaptureType)
</dd>
}
@if (Model.SensingMethod != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.SensingMethod)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.SensingMethod)
</dd>
}
@if (Model.Sharpness != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Sharpness)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Sharpness)
</dd>
}
@if (Model.SoftwareUsed != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.SoftwareUsed)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.SoftwareUsed)
</dd>
}
@if (Model.SubjectDistanceRange != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.SubjectDistanceRange)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.SubjectDistanceRange)
</dd>
}
@if (Model.VerticalResolution != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.VerticalResolution)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.VerticalResolution)
</dd>
}
@if (Model.WhiteBalance != null)
{
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.WhiteBalance)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.WhiteBalance)
</dd>
}
</dl>
</div>

View File

@@ -1,296 +0,0 @@
@using Marechai.Database
@model Marechai.Database.Models.MachinePhoto
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Machine photo</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger">
</div>
<div class="form-group">
<label asp-for="Author" class="control-label">
</label>
<input asp-for="Author" class="form-control" />
<span asp-validation-for="Author" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="CameraManufacturer" class="control-label">
</label>
<input asp-for="CameraManufacturer" class="form-control" />
<span asp-validation-for="CameraManufacturer" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="CameraModel" class="control-label">
</label>
<input asp-for="CameraModel" class="form-control" />
<span asp-validation-for="CameraModel" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ColorSpace" class="control-label">
</label>
<select asp-for="ColorSpace" class="form-control" asp-items="Html.GetEnumSelectList<ColorSpace>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="ColorSpace" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Comments" class="control-label">
</label>
<input asp-for="Comments" class="form-control" />
<span asp-validation-for="Comments" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Contrast" class="control-label">
</label>
<select asp-for="Contrast" class="form-control" asp-items="Html.GetEnumSelectList<Contrast>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="Contrast" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="CreationDate" class="control-label">
</label>
<input asp-for="CreationDate" class="form-control" />
<span asp-validation-for="CreationDate" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="DigitalZoomRatio" class="control-label">
</label>
<input asp-for="DigitalZoomRatio" class="form-control" />
<span asp-validation-for="DigitalZoomRatio" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ExifVersion" class="control-label">
</label>
<input asp-for="ExifVersion" class="form-control" />
<span asp-validation-for="ExifVersion" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ExposureTime" class="control-label">
</label>
<input asp-for="ExposureTime" class="form-control" />
<span asp-validation-for="ExposureTime" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ExposureMethod" class="control-label">
</label>
<select asp-for="ExposureMethod" class="form-control" asp-items="Html.GetEnumSelectList<ExposureMode>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="ExposureMethod" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ExposureProgram" class="control-label">
</label>
<select asp-for="ExposureProgram" class="form-control" asp-items="Html.GetEnumSelectList<ExposureProgram>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="ExposureProgram" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Flash" class="control-label">
</label>
<select asp-for="Flash" class="form-control" asp-items="Html.GetEnumSelectList<Flash>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="Flash" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Focal" class="control-label">
</label>
<input asp-for="Focal" class="form-control" />
<span asp-validation-for="Focal" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="FocalLength" class="control-label">
</label>
<input asp-for="FocalLength" class="form-control" />
<span asp-validation-for="FocalLength" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="FocalLengthEquivalent" class="control-label">
</label>
<input asp-for="FocalLengthEquivalent" class="form-control" />
<span asp-validation-for="FocalLengthEquivalent" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="HorizontalResolution" class="control-label">
</label>
<input asp-for="HorizontalResolution" class="form-control" />
<span asp-validation-for="HorizontalResolution" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="IsoRating" class="control-label">
</label>
<input asp-for="IsoRating" class="form-control" />
<span asp-validation-for="IsoRating" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Lens" class="control-label">
</label>
<input asp-for="Lens" class="form-control" />
<span asp-validation-for="Lens" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="License" class="control-label">
</label>
<select asp-for="LicenseId" class="form-control" asp-items="ViewBag.LicenseId">
</select>
<span asp-validation-for="License" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="LightSource" class="control-label">
</label>
<select asp-for="LightSource" class="form-control" asp-items="Html.GetEnumSelectList<LightSource>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="LightSource" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Machine" class="control-label">
</label>
<select asp-for="MachineId" class="form-control" asp-items="ViewBag.MachineId">
</select>
</div>
<div class="form-group">
<label asp-for="MeteringMode" class="control-label">
</label>
<select asp-for="MeteringMode" class="form-control" asp-items="Html.GetEnumSelectList<MeteringMode>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="MeteringMode" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ResolutionUnit" class="control-label">
</label>
<select asp-for="ResolutionUnit" class="form-control" asp-items="Html.GetEnumSelectList<ResolutionUnit>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="ResolutionUnit" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Orientation" class="control-label">
</label>
<select asp-for="Orientation" class="form-control" asp-items="Html.GetEnumSelectList<Orientation>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="Orientation" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Saturation" class="control-label">
</label>
<select asp-for="Saturation" class="form-control" asp-items="Html.GetEnumSelectList<Saturation>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="Saturation" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="SceneCaptureType" class="control-label">
</label>
<select asp-for="SceneCaptureType" class="form-control" asp-items="Html.GetEnumSelectList<SceneCaptureType>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="SceneCaptureType" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="SensingMethod" class="control-label">
</label>
<select asp-for="SensingMethod" class="form-control" asp-items="Html.GetEnumSelectList<SensingMethod>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="SensingMethod" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Sharpness" class="control-label">
</label>
<select asp-for="Sharpness" class="form-control" asp-items="Html.GetEnumSelectList<Sharpness>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="Sharpness" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="SoftwareUsed" class="control-label">
</label>
<input asp-for="SoftwareUsed" class="form-control" />
<span asp-validation-for="SoftwareUsed" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Source" class="control-label">
</label>
<input asp-for="Source" class="form-control" />
<span asp-validation-for="Source" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="SubjectDistanceRange" class="control-label">
</label>
<select asp-for="SubjectDistanceRange" class="form-control" asp-items="Html.GetEnumSelectList<SubjectDistanceRange>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="SubjectDistanceRange" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="VerticalResolution" class="control-label">
</label>
<input asp-for="VerticalResolution" class="form-control" />
<span asp-validation-for="VerticalResolution" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="WhiteBalance" class="control-label">
</label>
<select asp-for="WhiteBalance" class="form-control" asp-items="Html.GetEnumSelectList<WhiteBalance>().OrderBy(s => s.Text)">
<option value="">Unknown</option>
</select>
<span asp-validation-for="WhiteBalance" class="text-danger">
</span>
</div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Save" />
<a asp-action="Index" class="btn btn-secondary">
Back to List
</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@@ -1,77 +0,0 @@
@model IEnumerable<Marechai.Areas.Admin.Models.MachinePhotoViewModel>
@{
ViewData["Title"] = "Index";
}
<h1>Machine photos</h1>
<p>
<a asp-action="Create" class="btn btn-primary">
Create new
</a>
</p>
<table class="table">
<thead>
<tr>
<th></th>
<th>
@Html.DisplayNameFor(model => model.Machine)
</th>
<th>
@Html.DisplayNameFor(model => model.UploadUser)
</th>
<th>
@Html.DisplayNameFor(model => model.UploadDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Author)
</th>
<th>
@Html.DisplayNameFor(model => model.License)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td class="text-center">
<picture>
<source type="image/webp" srcset="/assets/photos/machines/thumbs/webp/1x/@(item.Id).webp,
/assets/photos/machines/thumbs/webp/2x/@(item.Id).webp 2x,
/assets/photos/machines/thumbs/webp/3x/@(item.Id).webp 3x">
<img srcset="/assets/photos/machines/thumbs/jpg/1x/@(item.Id).jpg,
/assets/photos/machines/thumbs/jpg/2x/@(item.Id).jpg 2x,
/assets/photos/machines/thumbs/jpg/3x/@(item.Id).jpg 3x" src="/assets/photos/machines/thumbs/jpg/1x/@(item.Id).jpg" ) alt="" height="auto" width="auto" style="max-height: 64px; max-width: 64px" />
</picture>
</td>
<td class="align-middle">
@Html.DisplayFor(modelItem => item.Machine)
</td>
<td class="align-middle">
@Html.DisplayFor(modelItem => item.UploadUser)
</td>
<td class="align-middle">
@Html.DisplayFor(modelItem => item.UploadDate)
</td>
<td class="align-middle">
@Html.DisplayFor(modelItem => item.Author)
</td>
<td class="align-middle">
@Html.DisplayFor(modelItem => item.License)
</td>
<td class="align-middle">
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-primary">
Details
</a>
<a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-secondary">
Edit
</a>
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">
Delete
</a>
</td>
</tr>
}
</tbody>
</table>

View File

@@ -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<Rgba32> 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);
}
}
}
}
}

111
Marechai/Helpers/Exif.cs Normal file
View File

@@ -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<DateTime?>
{
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"));
}
}
}

View File

@@ -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;
}
}
}
}

346
Marechai/Helpers/Photos.cs Normal file
View File

@@ -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<string> paths = new List<string>();
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<Task> pool = new List<Task>
{
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;
}
}

View File

@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Version>3.0.99.1405</Version>
<Version>3.0.99.1489</Version>
<Company>Canary Islands Computer Museum</Company>
<Copyright>Copyright © 2003-2020 Natalia Portillo</Copyright>
<Product>Canary Islands Computer Museum Website</Product>
@@ -131,6 +131,9 @@
<Content Update="Pages\Admin\Details\MachinePhoto.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Pages\Admin\Details\CreateMachinePhoto.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
<ItemGroup>
<_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" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,856 @@
@{
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Filename : Details.cshtml
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
}
@page "/admin/machines/photo/create/{MachineId:int}"
@using Marechai.Database
@using Marechai.Database.Models
@inherits OwningComponentBase<MachinePhotosService>
@inject IStringLocalizer<MachinePhotosService> L
@inject NavigationManager NavigationManager
@inject LicensesService LicensesService
@inject IFileReaderService FileReaderService;
@inject MachinesService MachinesService;
@inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IWebHostEnvironment Host
@attribute [Authorize(Roles = "UberAdmin, Admin")]
@if (!_loaded)
{
<p align="center">@L["Loading..."]</p>
return;
}
<h3>@string.Format(L["Upload photo for machine {0} manufactured by {1}"], _machine.Name, _machine.Company)</h3>
<hr />
@if(!_uploaded)
{
@if(!_uploading)
{
<FieldLabel>@L["Choose photo file"]</FieldLabel>
<input type="file" @ref=_inputUpload /><br/>
<Field>
<FieldLabel>@L["License"]</FieldLabel>
<Select TValue="int" @bind-SelectedValue="@_licenseId">
@foreach (var license in _licenses)
{
<SelectItem TValue="int" Value="@license.Id">@license.Name</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["Source URL"]</FieldLabel>
<Check TValue="bool" @bind-Checked="@_unknownSource">@L["Unknown (source url)"]</Check>
@if (!_unknownSource)
{
<Validation Validator="@ValidateSource">
<TextEdit @bind-Text="@_sourceUrl">
<Feedback>
<ValidationError>@L["Please enter a valid source URL."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
<div>TODO: Put legal disclaimer here</div>
<Button Color="Color.Success" Clicked="@UploadFile" Disabled="@_uploading">@L["Upload"]</Button><br/>
}
else
{
@L["Uploading..."]
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: @(_progressValue)%;" aria-valuenow="@_progressValue" aria-valuemin="0" aria-valuemax="100">@($"{_progressValue:F2}")%</div>
</div>
}
@if(_uploadError)
{
<span class="text-danger">@_uploadErrorMessage</span>
}
}
else
{
<h5>@string.Format(L["Image format recognized as {0}"], _imageFormat)</h5>
@if (_allFinished)
{
<span class="text-success">@L["All finished!"]</span>
<a class="btn btn-success" href="/admin/machines/photo/details/@_model.Id">@L["Go to photo details"]</a>
}
<table>
<tr>
<td>
@L["Extracting Exif information..."]
</td>
<td>
@if(_extractExif == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_extractExif == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
</tr>
<tr>
<td>
@L["Moving file to proper place..."]
</td>
<td>
@if(_moveFile == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_moveFile == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_moveFile == true)
{
<a href="/assets/photos/machines/originals/@($"{_model.Id}{_model.OriginalExtension}")" target="_blank">
<img src="/assets/photos/machines/originals/@($"{_model.Id}{_model.OriginalExtension}")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with an HD resolution (thumbnail)..."]
</td>
<td>
@if(_convertJpegHdTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpegHdTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpegHdTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/jpeg/hd/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jpeg/hd/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with a 1440p resolution (thumbnail)..."]
</td>
<td>
@if(_convertJpeg1440Th == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpeg1440Th == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpeg1440Th == true)
{
<a href="@($"/assets/photos/machines/thumbs/jpeg/1440p/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jpeg/1440p/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with a 4K resolution (thumbnail)..."]
</td>
<td>
@if(_convertJpeg4kTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpeg4kTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpeg4kTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/jpeg/4k/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jpeg/4k/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with an HD resolution..."]
</td>
<td>
@if(_convertJpegHd == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpegHd == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpegHd == true)
{
<a href="@($"/assets/photos/machines/jpeg/hd/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/jpeg/hd/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with a 1440p resolution..."]
</td>
<td>
@if(_convertJpeg1440 == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpeg1440 == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpeg1440 == true)
{
<a href="@($"/assets/photos/machines/jpeg/1440p/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/jpeg/1440p/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG with a 4K resolution..."]
</td>
<td>
@if(_convertJpeg4k == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJpeg4k == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJpeg4k == true)
{
<a href="@($"/assets/photos/machines/jpeg/4k/{_model.Id}.jpg")" target="_blank">
<img src="@($"/assets/photos/machines/jpeg/4k/{_model.Id}.jpg")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with an HD resolution (thumbnail)..."]
</td>
<td>
@if(_convertJp2kHdTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2kHdTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2kHdTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/jp2k/hd/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jp2k/hd/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with a 1440p resolution (thumbnail)..."]
</td>
<td>
@if(_convertJp2k1440Th == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2k1440Th == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2k1440Th == true)
{
<a href="@($"/assets/photos/machines/thumbs/jp2k/1440p/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jp2k/1440p/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with a 4K resolution (thumbnail)..."]
</td>
<td>
@if(_convertJp2k4kTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2k4kTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2k4kTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/jp2k/4k/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/jp2k/4k/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with an HD resolution..."]
</td>
<td>
@if(_convertJp2kHd == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2kHd == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2kHd == true)
{
<a href="@($"/assets/photos/machines/jp2k/hd/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/jp2k/hd/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with a 1440p resolution..."]
</td>
<td>
@if(_convertJp2k1440 == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2k1440 == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2k1440 == true)
{
<a href="@($"/assets/photos/machines/jp2k/1440p/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/jp2k/1440p/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to JPEG2000 with a 4K resolution..."]
</td>
<td>
@if(_convertJp2k4k == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertJp2k4k == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertJp2k4k == true)
{
<a href="@($"/assets/photos/machines/jp2k/4k/{_model.Id}.jp2")" target="_blank">
<img src="@($"/assets/photos/machines/jp2k/4k/{_model.Id}.jp2")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with an HD resolution (thumbnail)..."]
</td>
<td>
@if(_convertWebpHdTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebpHdTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebpHdTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/webp/hd/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/webp/hd/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with a 1440p resolution (thumbnail)..."]
</td>
<td>
@if(_convertWebp1440Th == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebp1440Th == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebp1440Th == true)
{
<a href="@($"/assets/photos/machines/thumbs/webp/1440p/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/webp/1440p/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with a 4K resolution (thumbnail)..."]
</td>
<td>
@if(_convertWebp4kTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebp4kTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebp4kTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/webp/4k/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/webp/4k/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with an HD resolution..."]
</td>
<td>
@if(_convertWebpHd == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebpHd == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebpHd == true)
{
<a href="@($"/assets/photos/machines/webp/hd/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/webp/hd/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with a 1440p resolution..."]
</td>
<td>
@if(_convertWebp1440 == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebp1440 == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebp1440 == true)
{
<a href="@($"/assets/photos/machines/webp/1440p/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/webp/1440p/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to WebP with a 4K resolution..."]
</td>
<td>
@if(_convertWebp4k == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertWebp4k == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertWebp4k == true)
{
<a href="@($"/assets/photos/machines/webp/4k/{_model.Id}.webp")" target="_blank">
<img src="@($"/assets/photos/machines/webp/4k/{_model.Id}.webp")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with an HD resolution (thumbnail)..."]
</td>
<td>
@if(_convertHeifHdTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeifHdTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeifHdTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/heif/hd/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/heif/hd/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with a 1440p resolution (thumbnail)..."]
</td>
<td>
@if(_convertHeif1440Th == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeif1440Th == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeif1440Th == true)
{
<a href="@($"/assets/photos/machines/thumbs/heif/1440p/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/heif/1440p/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with a 4K resolution (thumbnail)..."]
</td>
<td>
@if(_convertHeif4kTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeif4kTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeif4kTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/heif/4k/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/heif/4k/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with an HD resolution..."]
</td>
<td>
@if(_convertHeifHd == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeifHd == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeifHd == true)
{
<a href="@($"/assets/photos/machines/heif/hd/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/heif/hd/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with a 1440p resolution..."]
</td>
<td>
@if(_convertHeif1440 == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeif1440 == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeif1440 == true)
{
<a href="@($"/assets/photos/machines/heif/1440p/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/heif/1440p/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to HEIF with a 4K resolution..."]
</td>
<td>
@if(_convertHeif4k == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertHeif4k == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertHeif4k == true)
{
<a href="@($"/assets/photos/machines/heif/4k/{_model.Id}.heic")" target="_blank">
<img src="@($"/assets/photos/machines/heif/4k/{_model.Id}.heic")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with an HD resolution (thumbnail)..."]
</td>
<td>
@if(_convertAvifHdTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvifHdTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvifHdTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/avif/hd/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/avif/hd/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with a 1440p resolution (thumbnail)..."]
</td>
<td>
@if(_convertAvif1440Th == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvif1440Th == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvif1440Th == true)
{
<a href="@($"/assets/photos/machines/thumbs/avif/1440p/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/avif/1440p/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with a 4K resolution (thumbnail)..."]
</td>
<td>
@if(_convertAvif4kTh == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvif4kTh == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvif4kTh == true)
{
<a href="@($"/assets/photos/machines/thumbs/avif/4k/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/thumbs/avif/4k/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with an HD resolution..."]
</td>
<td>
@if(_convertAvifHd == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvifHd == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvifHd == true)
{
<a href="@($"/assets/photos/machines/avif/hd/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/avif/hd/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with a 1440p resolution..."]
</td>
<td>
@if(_convertAvif1440 == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvif1440 == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvif1440 == true)
{
<a href="@($"/assets/photos/machines/avif/1440p/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/avif/1440p/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Converting photo to AV1F with a 4K resolution..."]
</td>
<td>
@if(_convertAvif4k == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_convertAvif4k == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
@if(_convertAvif4k == true)
{
<a href="@($"/assets/photos/machines/avif/4k/{_model.Id}.avif")" target="_blank">
<img src="@($"/assets/photos/machines/avif/4k/{_model.Id}.avif")" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
</a>
}
</td>
</tr>
<tr>
<td>
@L["Adding photo to database..."]
</td>
<td>
@if(_addToDatabase == true)
{
<span class="text-success">@L["OK!"]</span>
}
else if(_addToDatabase == false)
{
<span class="text-danger">@L["Error!"]</span>
}
</td>
<td>
</td>
</tr>
</table>
}

View File

@@ -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<Database.Models.License> _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<Exif[]>(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);
}
}

View File

@@ -49,6 +49,7 @@
@inject StorageByMachineService StorageByMachineService
@inject ScreensByMachineService ScreensByMachineService
@inject ScreensService ScreensService
@inject MachinePhotosService MachinePhotosService
@attribute [Authorize(Roles = "UberAdmin, Admin")]
<h3>@L["Machine details"]</h3>
<hr />

View File

@@ -61,6 +61,7 @@ namespace Marechai.Pages.Admin.Details
List<StorageByMachineViewModel> _machineStorage;
MachineViewModel _model;
bool _noFamily;
List<Guid> _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/",

View File

@@ -0,0 +1,769 @@
@{
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Filename : Details.cshtml
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// 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<MachinePhotosService>
@inject IStringLocalizer<MachinePhotosService> L
@inject NavigationManager NavigationManager
@inject LicensesService LicensesService
@inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> UserManager
@attribute [Authorize(Roles = "UberAdmin, Admin")]
<h3>@L["Machine photo details"]</h3>
<hr />
@if (!_loaded)
{
<p align="center">@L["Loading..."]</p>
return;
}
<div>
<Field>
<FieldLabel>@L["Machine"]</FieldLabel>
<TextEdit Disabled="true" Text="@($"{_model.MachineCompanyName} - {_model.MachineName}")"/>
</Field>
<Field>
<FieldLabel>@L["Uploaded by"]</FieldLabel>
<TextEdit Disabled="true" Text="@($"{_user.UserName} <{_user.Email}>")"/>
</Field>
<Field>
<FieldLabel>@L["Uploaded date"]</FieldLabel>
<TextEdit Disabled="true" Text="@($"{_model.UploadDate}")"/>
</Field>
<Field>
<FieldLabel>@L["License"]</FieldLabel>
<Select Disabled="!_editing" TValue="int" @bind-SelectedValue="@_model.LicenseId">
@foreach (var license in _licenses)
{
<SelectItem TValue="int" Value="@license.Id">@license.Name</SelectItem>
}
</Select>
</Field>
@if (_editing || _model.Author != null)
{
<Field>
<FieldLabel>@L["Author"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownAuthor">@L["Unknown (author)"]</Check>
}
@if (!_editing ||
!_unknownSource)
{
<Validation Validator="@ValidateAuthor">
<TextEdit Disabled="!_editing" @bind-Text="@_model.Author">
<Feedback>
<ValidationError>@L["Please enter a valid author."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.Source != null)
{
<Field>
<FieldLabel>@L["Source URL"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSource">@L["Unknown (source url)"]</Check>
}
@if (!_editing ||
!_unknownSource)
{
<Validation Validator="@ValidateSource">
<TextEdit Disabled="!_editing" @bind-Text="@_model.Source">
<Feedback>
<ValidationError>@L["Please enter a valid source URL."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.Aperture != null)
{
<Field>
<FieldLabel>@L["Aperture"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownAperture">@L["Unknown (aperture)"]</Check>
}
@if (!_editing ||
!_unknownAperture)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.Aperture">
<Feedback>
<ValidationError>@L["Please enter a valid aperture."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.CameraManufacturer != null)
{
<Field>
<FieldLabel>@L["Camera manufacturer"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownCameraManufacturer">@L["Unknown (camera manufacturer)"]</Check>
}
@if (!_editing ||
!_unknownCameraManufacturer)
{
<Validation Validator="@ValidateCameraManufacturer">
<TextEdit Disabled="!_editing" @bind-Text="@_model.CameraManufacturer">
<Feedback>
<ValidationError>@L["Please enter a valid camera manufacturer."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.CameraModel != null)
{
<Field>
<FieldLabel>@L["Camera model"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownCameraModel">@L["Unknown (camera model)"]</Check>
}
@if (!_editing ||
!_unknownCameraModel)
{
<Validation Validator="@ValidateCameraModel">
<TextEdit Disabled="!_editing" @bind-Text="@_model.CameraModel">
<Feedback>
<ValidationError>@L["Please enter a valid camera model."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.ColorSpace != null)
{
<Field>
<FieldLabel>@L["Color space"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownColorSpace">@L["Unknown (color space)"]</Check>
}
@if (!_editing ||
!_unknownColorSpace)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@ColorSpace">
@foreach (ushort value in Enum.GetValues(typeof(ColorSpace)))
{
<SelectItem TValue="ushort" Value="@value">@(((ColorSpace)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Contrast != null)
{
<Field>
<FieldLabel>@L["Contrast"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownContrast">@L["Unknown (contrast)"]</Check>
}
@if (!_editing ||
!_unknownContrast)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@Contrast">
@foreach (ushort value in Enum.GetValues(typeof(Contrast)))
{
<SelectItem TValue="ushort" Value="@value">@(((Contrast)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.CreationDate != null)
{
<Field>
<FieldLabel>@L["Creation date"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownCreationDate">@L["Unknown (creation date)"]</Check>
}
@if (!_editing ||
!_unknownCreationDate)
{
<Validation Validator="@ValidateDate">
<DateEdit Disabled="!_editing" TValue="DateTime?" @bind-Date="@_model.CreationDate">
<Feedback>
<ValidationError>@L["Please enter a correct creation date."]</ValidationError>
</Feedback>
</DateEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.DigitalZoomRatio != null)
{
<Field>
<FieldLabel>@L["Digital zoom ratio"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownDigitalZoomRatio">@L["Unknown (digital zoom ratio)"]</Check>
}
@if (!_editing ||
!_unknownDigitalZoomRatio)
{
<Validation Validator="@ValidateDoubleBiggerOrEqualThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.DigitalZoomRatio">
<Feedback>
<ValidationError>@L["Please enter a valid digital zoom ratio."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.ExifVersion != null)
{
<Field>
<FieldLabel>@L["Exif version"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownExifVersion">@L["Unknown (exif version)"]</Check>
}
@if (!_editing ||
!_unknownExifVersion)
{
<Validation Validator="@ValidateExifVersion">
<TextEdit Disabled="!_editing" @bind-Text="@_model.ExifVersion">
<Feedback>
<ValidationError>@L["Please enter a valid Exif version."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.ExposureTime != null)
{
<Field>
<FieldLabel>@L["Exposure time"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownExposure">@L["Unknown (exposure time)"]</Check>
}
@if (!_editing ||
!_unknownExposure)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.ExposureTime">
<Feedback>
<ValidationError>@L["Please enter a valid exposure time."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.ExposureMethod != null)
{
<Field>
<FieldLabel>@L["Exposure mode"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownExposureMethod">@L["Unknown (exposure mode)"]</Check>
}
@if (!_editing ||
!_unknownExposureMethod)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@ExposureMode">
@foreach (ushort value in Enum.GetValues(typeof(ExposureMode)))
{
<SelectItem TValue="ushort" Value="@value">@(((ExposureMode)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.ExposureProgram != null)
{
<Field>
<FieldLabel>@L["Exposure program"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownExposureProgram">@L["Unknown (exposure program)"]</Check>
}
@if (!_editing ||
!_unknownExposureProgram)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@ExposureProgram">
@foreach (ushort value in Enum.GetValues(typeof(ExposureProgram)))
{
<SelectItem TValue="ushort" Value="@value">@(((ExposureProgram)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Flash != null)
{
<Field>
<FieldLabel>@L["Flash"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownFlash">@L["Unknown (flash)"]</Check>
}
@if (!_editing ||
!_unknownFlash)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@Flash">
@foreach (ushort value in Enum.GetValues(typeof(Flash)))
{
<SelectItem TValue="ushort" Value="@value">@(((Flash)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Focal != null)
{
<Field>
<FieldLabel>@L["F-number"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownFocal">@L["Unknown (f-number)"]</Check>
}
@if (!_editing ||
!_unknownFocal)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.Focal">
<Feedback>
<ValidationError>@L["Please enter a valid focal number."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.FocalLength != null)
{
<Field>
<FieldLabel>@L["Focal length"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownFocalLength">@L["Unknown (focal length)"]</Check>
}
@if (!_editing ||
!_unknownFocalLength)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.FocalLength">
<Feedback>
<ValidationError>@L["Please enter a valid focal length."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.FocalLengthEquivalent != null)
{
<Field>
<FieldLabel>@L["Focal length in 35mm film"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownFocalLengthEquivalent">@L["Unknown (focal length in 35mm film)"]</Check>
}
@if (!_editing ||
!_unknownFocalLengthEquivalent)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.FocalLengthEquivalent">
<Feedback>
<ValidationError>@L["Please enter a valid focal length in 35mm film."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.HorizontalResolution != null)
{
<Field>
<FieldLabel>@L["Horizontal resolution"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownHorizontalResolution">@L["Unknown (horizontal resolution)"]</Check>
}
@if (!_editing ||
!_unknownHorizontalResolution)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.HorizontalResolution">
<Feedback>
<ValidationError>@L["Please enter a valid horizontal resolution."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.IsoRating != null)
{
<Field>
<FieldLabel>@L["ISO rating"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownIsoRating">@L["Unknown (ISO rating)"]</Check>
}
@if (!_editing ||
!_unknownIsoRating)
{
<Validation Validator="@ValidateUnsignedShortBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="ushort?" @bind-Value="@_model.IsoRating">
<Feedback>
<ValidationError>@L["Please enter a valid ISO rating."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.Lens != null)
{
<Field>
<FieldLabel>@L["Lens"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownLens">@L["Unknown (lens)"]</Check>
}
@if (!_editing ||
!_unknownLens)
{
<Validation Validator="@ValidateLens">
<TextEdit Disabled="!_editing" @bind-Text="@_model.Lens">
<Feedback>
<ValidationError>@L["Please enter a valid lens."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.LightSource != null)
{
<Field>
<FieldLabel>@L["Light source"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownLightSource">@L["Unknown (light source)"]</Check>
}
@if (!_editing ||
!_unknownLightSource)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@LightSource">
@foreach (ushort value in Enum.GetValues(typeof(LightSource)))
{
<SelectItem TValue="ushort" Value="@value">@(((LightSource)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.MeteringMode != null)
{
<Field>
<FieldLabel>@L["Metering mode"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownMeteringMode">@L["Unknown (metering mode)"]</Check>
}
@if (!_editing ||
!_unknownMeteringMode)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@MeteringMode">
@foreach (ushort value in Enum.GetValues(typeof(MeteringMode)))
{
<SelectItem TValue="ushort" Value="@value">@(((MeteringMode)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.ResolutionUnit != null)
{
<Field>
<FieldLabel>@L["Resolution unit"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownResolutionUnit">@L["Unknown (resolution unit)"]</Check>
}
@if (!_editing ||
!_unknownResolutionUnit)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@ResolutionUnit">
@foreach (ushort value in Enum.GetValues(typeof(ResolutionUnit)))
{
<SelectItem TValue="ushort" Value="@value">@(((ResolutionUnit)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Orientation != null)
{
<Field>
<FieldLabel>@L["Orientation"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownOrientation">@L["Unknown (orientation)"]</Check>
}
@if (!_editing ||
!_unknownOrientation)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@Orientation">
@foreach (ushort value in Enum.GetValues(typeof(Marechai.Database.Orientation)))
{
<SelectItem TValue="ushort" Value="@value">@(((Marechai.Database.Orientation)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Saturation != null)
{
<Field>
<FieldLabel>@L["Saturation"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSaturation">@L["Unknown (saturation)"]</Check>
}
@if (!_editing ||
!_unknownSaturation)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@Saturation">
@foreach (ushort value in Enum.GetValues(typeof(Saturation)))
{
<SelectItem TValue="ushort" Value="@value">@(((Saturation)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.SceneCaptureType != null)
{
<Field>
<FieldLabel>@L["Scene capture type"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSceneCaptureType">@L["Unknown (scene capture type)"]</Check>
}
@if (!_editing ||
!_unknownSceneCaptureType)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@SceneCaptureType">
@foreach (ushort value in Enum.GetValues(typeof(SceneCaptureType)))
{
<SelectItem TValue="ushort" Value="@value">@(((SceneCaptureType)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.SensingMethod != null)
{
<Field>
<FieldLabel>@L["Sensing method"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSensingMethod">@L["Unknown (sensing method)"]</Check>
}
@if (!_editing ||
!_unknownSensingMethod)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@SensingMethod">
@foreach (ushort value in Enum.GetValues(typeof(SensingMethod)))
{
<SelectItem TValue="ushort" Value="@value">@(((SensingMethod)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Sharpness != null)
{
<Field>
<FieldLabel>@L["Sharpness"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSharpness">@L["Unknown (sharpness)"]</Check>
}
@if (!_editing ||
!_unknownSharpness)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@Sharpness">
@foreach (ushort value in Enum.GetValues(typeof(Sharpness)))
{
<SelectItem TValue="ushort" Value="@value">@(((Sharpness)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.SoftwareUsed != null)
{
<Field>
<FieldLabel>@L["Software used"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSoftwareUsed">@L["Unknown (software used)"]</Check>
}
@if (!_editing ||
!_unknownSoftwareUsed)
{
<Validation Validator="@ValidateSoftwareUsed">
<TextEdit Disabled="!_editing" @bind-Text="@_model.SoftwareUsed">
<Feedback>
<ValidationError>@L["Please enter a valid software used."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.SubjectDistanceRange != null)
{
<Field>
<FieldLabel>@L["Subject distance range"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownSubjectDistanceRange">@L["Unknown (subject distance range)"]</Check>
}
@if (!_editing ||
!_unknownSubjectDistanceRange)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@SubjectDistanceRange">
@foreach (ushort value in Enum.GetValues(typeof(SubjectDistanceRange)))
{
<SelectItem TValue="ushort" Value="@value">@(((SubjectDistanceRange)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.VerticalResolution != null)
{
<Field>
<FieldLabel>@L["Vertical resolution"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownVerticalResolution">@L["Unknown (vertical resolution)"]</Check>
}
@if (!_editing ||
!_unknownVerticalResolution)
{
<Validation Validator="@ValidateDoubleBiggerThanZero">
<NumericEdit Disabled="!_editing" TValue="double?" Decimals="3" @bind-Value="@_model.VerticalResolution">
<Feedback>
<ValidationError>@L["Please enter a valid vertical resolution."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.WhiteBalance != null)
{
<Field>
<FieldLabel>@L["White balance"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownWhiteBalance">@L["Unknown (white balance)"]</Check>
}
@if (!_editing ||
!_unknownWhiteBalance)
{
<Select Disabled="!_editing" TValue="ushort" @bind-SelectedValue="@WhiteBalance">
@foreach (ushort value in Enum.GetValues(typeof(WhiteBalance)))
{
<SelectItem TValue="ushort" Value="@value">@(((WhiteBalance)value).ToString())</SelectItem>
}
</Select>
}
</Field>
}
@if (_editing || _model.Comments != null)
{
<Field>
<FieldLabel>@L["User comments"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownComments">@L["Unknown or empty (user comments)"]</Check>
}
@if (!_editing ||
!_unknownComments)
{
<Validation Validator="@ValidateComments">
<TextEdit Disabled="!_editing" @bind-Text="@_model.Comments">
<Feedback>
<ValidationError>@L["Please enter valid comments."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
</div>
<a href="/assets/photos/machines/originals/@($"{_model.Id}{_model.OriginalExtension}")" target="_blank">
<img src="/assets/photos/machines/originals/@($"{_model.Id}{_model.OriginalExtension}")" alt="" height="auto" width="auto" style="max-height: 512px; max-width: 512px" />
</a>
<div>
@if (!_editing)
{
<Button Color="Color.Primary" Clicked="@OnEditClicked">@L["Edit"]</Button>
}
else
{
<Button Color="Color.Success" Clicked="@OnSaveClicked">@L["Save"]</Button>
<Button Color="Color.Danger" Clicked="@OnCancelClicked">@L["Cancel"]</Button>
}
<a href="/admin/machines/details/@_model.MachineId" class="btn btn-secondary">@L["Back to machine"]</a>
</div>

View File

@@ -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<Database.Models.License> _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);
}
}

View File

@@ -62,7 +62,7 @@
@foreach (var computer in _computers)
{
<a href="/machine/@computer.Id">
@computer.CompanyName @computer.Name</a>
@computer.Company @computer.Name</a>
<br />
}
</p>

View File

@@ -62,7 +62,7 @@
@foreach (var console in _consoles)
{
<a href="/machine/@console.Id">
@console.CompanyName @console.Name</a>
@console.Company @console.Name</a>
<br />
}
</p>

View File

@@ -77,7 +77,7 @@
}
<b>
<a href="/company/@_machine.CompanyId">
@_machine.CompanyName</a> @_machine.Name</b>
@_machine.Company</a> @_machine.Name</b>
<table width="100%">
@if (_machine.Introduced.HasValue &&

View File

@@ -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",

View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ReSharper disable MarkupTextTypo -->
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Unknown (source url)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo's source URL</comment>
</data>
<data name="Unknown (author)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo author</comment>
</data>
<data name="Unknown (aperture)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo aperture</comment>
</data>
<data name="Unknown (camera manufacturer)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a camera manufacturer</comment>
</data>
<data name="Unknown (camera model)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a camera model</comment>
</data>
<data name="Unknown (color space)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo color space</comment>
</data>
<data name="Unknown (contrast)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo contrast</comment>
</data>
<data name="Unknown (creation date)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo creation date</comment>
</data>
<data name="Unknown (digital zoom ratio)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo digital zoom ratio</comment>
</data>
<data name="Unknown (exif version)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo exif version</comment>
</data>
<data name="Unknown (exposure time)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo exposure time</comment>
</data>
<data name="Unknown (exposure mode)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo exposure mode</comment>
</data>
<data name="Unknown (exposure program)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo exposure program</comment>
</data>
<data name="Unknown (flash)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a photo flash</comment>
</data>
<data name="Unknown (f-number)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a focal number</comment>
</data>
<data name="Unknown (focal length)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a focal length</comment>
</data>
<data name="Unknown (focal length in 35mm film)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a focal length</comment>
</data>
<data name="Unknown (horizontal resolution)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a resolution</comment>
</data>
<data name="Unknown (ISO rating)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to an ISO rating</comment>
</data>
<data name="Unknown (lens)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a lens model or name</comment>
</data>
<data name="Unknown (light source)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a light source</comment>
</data>
<data name="Unknown (metering mode)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a metering mode</comment>
</data>
<data name="Unknown (resolution unit)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a resolution unit</comment>
</data>
<data name="Unknown (orientation)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a orientation</comment>
</data>
<data name="Unknown (saturation)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a saturation</comment>
</data>
<data name="Unknown (scene capture type)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a scene capture type</comment>
</data>
<data name="Unknown (sensing method)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a sensing method</comment>
</data>
<data name="Unknown (sharpness)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a sharpness</comment>
</data>
<data name="Unknown (software used)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a software used</comment>
</data>
<data name="Unknown (subject distance range)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a subject distance range</comment>
</data>
<data name="Unknown (vertical resolution)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a vertical resolution</comment>
</data>
<data name="Unknown (white balance)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to a white balance</comment>
</data>
<data name="Unknown or empty (user comments)" xml:space="preserve">
<value>Unknown</value>
<comment>Unknown, referring to user comments</comment>
</data>
</root>

View File

@@ -0,0 +1,709 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ReSharper disable MarkupTextTypo -->
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Unknown (source url)" xml:space="preserve">
<value>Descconocida</value>
<comment>Unknown, referring to a photo's source URL</comment>
</data>
<data name="Unknown (author)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo author</comment>
</data>
<data name="Unknown (aperture)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a photo aperture</comment>
</data>
<data name="Unknown (camera manufacturer)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a camera manufacturer</comment>
</data>
<data name="Unknown (camera model)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a camera model</comment>
</data>
<data name="Unknown (color space)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo color space</comment>
</data>
<data name="Unknown (contrast)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo contrast</comment>
</data>
<data name="Unknown (creation date)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a photo creation date</comment>
</data>
<data name="Unknown (digital zoom ratio)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a photo digital zoom ratio</comment>
</data>
<data name="Unknown (exif version)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a photo exif version</comment>
</data>
<data name="Unknown (exposure time)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo exposure time</comment>
</data>
<data name="Unknown (exposure mode)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo exposure mode</comment>
</data>
<data name="Unknown (exposure program)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo exposure program</comment>
</data>
<data name="Unknown (flash)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a photo flash</comment>
</data>
<data name="Unknown (f-number)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a focal number</comment>
</data>
<data name="Unknown (focal length)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a focal length</comment>
</data>
<data name="Unknown (focal length in 35mm film)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a focal length</comment>
</data>
<data name="Unknown (horizontal resolution)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a resolution</comment>
</data>
<data name="Unknown (ISO rating)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to an ISO rating</comment>
</data>
<data name="Unknown (lens)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a lens model or name</comment>
</data>
<data name="Unknown (light source)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a light source</comment>
</data>
<data name="Unknown (metering mode)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a metering mode</comment>
</data>
<data name="Unknown (resolution unit)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a resolution unit</comment>
</data>
<data name="Unknown (orientation)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a orientation</comment>
</data>
<data name="Unknown (saturation)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a saturation</comment>
</data>
<data name="Unknown (scene capture type)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a scene capture type</comment>
</data>
<data name="Unknown (sensing method)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a sensing method</comment>
</data>
<data name="Unknown (sharpness)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a sharpness</comment>
</data>
<data name="Unknown (software used)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a software used</comment>
</data>
<data name="Unknown (subject distance range)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a subject distance range</comment>
</data>
<data name="Unknown (vertical resolution)" xml:space="preserve">
<value>Desconocida</value>
<comment>Unknown, referring to a vertical resolution</comment>
</data>
<data name="Unknown (white balance)" xml:space="preserve">
<value>Desconocido</value>
<comment>Unknown, referring to a white balance</comment>
</data>
<data name="Unknown or empty (user comments)" xml:space="preserve">
<value>Desconocidos o ninguno</value>
<comment>Unknown, referring to user comments</comment>
</data>
<data name="Loading..." xml:space="preserve">
<value>Cargando...</value>
<comment>Loading...</comment>
</data>
<data name="Upload photo for machine {0} manufactured by {1}" xml:space="preserve">
<value>Subir foto para la máquina {0} fabricada por {1}</value>
<comment>Upload photo for machine {0} manufactured by {1}</comment>
</data>
<data name="Choose photo file" xml:space="preserve">
<value>Elige archivo de foto</value>
<comment>Choose photo file</comment>
</data>
<data name="License" xml:space="preserve">
<value>Licencia</value>
<comment>License</comment>
</data>
<data name="Source URL" xml:space="preserve">
<value>Dirección de origen</value>
<comment>Source URL</comment>
</data>
<data name="Please enter a valid source URL." xml:space="preserve">
<value>Por favor introduce una dirección de origen válida.</value>
<comment>Please enter a valid source URL.</comment>
</data>
<data name="Upload" xml:space="preserve">
<value>Subir</value>
<comment>Upload</comment>
</data>
<data name="Image format recognized as {0}" xml:space="preserve">
<value>Formato de imagen reconocido como {0}</value>
<comment>Image format recognized as {0}</comment>
</data>
<data name="All finished!" xml:space="preserve">
<value>¡Terminado!</value>
<comment>All finished!</comment>
</data>
<data name="Go to photo details" xml:space="preserve">
<value>Ir a detalles de la foto</value>
<comment>Go to photo details</comment>
</data>
<data name="Extracting Exif information..." xml:space="preserve">
<value>Extrayendo información Exif...</value>
<comment>Extracting Exif information...</comment>
</data>
<data name="OK!" xml:space="preserve">
<value>¡OK!</value>
<comment>OK!</comment>
</data>
<data name="Error!" xml:space="preserve">
<value>¡Error!</value>
<comment>Error!</comment>
</data>
<data name="Moving file to proper place..." xml:space="preserve">
<value>Moviendo el archivo al lugar adecuado...</value>
<comment>Moving file to proper place...</comment>
</data>
<data name="Converting photo to JPEG with an HD resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución HD (miniatura)...</value>
<comment>Converting photo to JPEG with an HD resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG with a 1440p resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución 1440p (miniatura)...</value>
<comment>Converting photo to JPEG with a 1440p resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG with a 4K resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución 4K (miniatura)...</value>
<comment>Converting photo to JPEG with a 4K resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG with an HD resolution..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución HD...</value>
<comment>Converting photo to JPEG with an HD resolution...</comment>
</data>
<data name="Converting photo to JPEG with a 1440p resolution..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución 1440p...</value>
<comment>Converting photo to JPEG with a 1440p resolution...</comment>
</data>
<data name="Converting photo to JPEG with a 4K resolution..." xml:space="preserve">
<value>Convirtiendo foto a JPEG con una resolución 4K...</value>
<comment>Converting photo to JPEG with a 4K resolution...</comment>
</data>
<data name="Converting photo to JPEG2000 with an HD resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG2000 con una resolución HD (miniatura)...</value>
<comment>Converting photo to JPEG2000 with an HD resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG2000 with a 1440p resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG2000 con una resolución 1440p (miniatura)...</value>
<comment>Converting photo to JPEG2000 with a 1440p resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG2000 with a 4K resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a JPEG2000 con una resolución 4K (miniatura)...</value>
<comment>Converting photo to JPEG2000 with a 4K resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to JPEG2000 with an HD resolution..." xml:space="preserve">
<value>Convirtiendo foto a JPEG2000 con una resolución HD...</value>
<comment>Converting photo to JPEG2000 with an HD resolution...</comment>
</data>
<data name="Converting photo to JPEG2000 with a 1440p resolution..." xml:space="preserve">
<value>AAAConvirtiendo foto a JPEG2000 con una resolución 1440p...</value>
<comment>Converting photo to JPEG2000 with a 1440p resolution...</comment>
</data>
<data name="Converting photo to JPEG2000 with a 4K resolution..." xml:space="preserve">
<value>Convirtiendo foto a JPEG2000 con una resolución 4K...</value>
<comment>Converting photo to JPEG2000 with a 4K resolution...</comment>
</data>
<data name="Converting photo to WebP with an HD resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución HD (miniatura)...</value>
<comment>Converting photo to WebP with an HD resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to WebP with a 1440p resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución 1440p (miniatura)...</value>
<comment>Converting photo to WebP with a 1440p resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to WebP with a 4K resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución 4K (miniatura)...</value>
<comment>Converting photo to WebP with a 4K resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to WebP with an HD resolution..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución HD...</value>
<comment>Converting photo to WebP with an HD resolution...</comment>
</data>
<data name="Converting photo to WebP with a 1440p resolution..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución 1440p...</value>
<comment>Converting photo to WebP with a 1440p resolution...</comment>
</data>
<data name="Converting photo to WebP with a 4K resolution..." xml:space="preserve">
<value>Convirtiendo foto a WebP con una resolución 4K...</value>
<comment>Converting photo to WebP with a 4K resolution...</comment>
</data>
<data name="Converting photo to HEIF with an HD resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución HD (miniatura)...</value>
<comment>Converting photo to HEIF with an HD resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to HEIF with a 1440p resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución 1440p (miniatura)...</value>
<comment>Converting photo to HEIF with a 1440p resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to HEIF with a 4K resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución 4K (miniatura)...</value>
<comment>Converting photo to HEIF with a 4K resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to HEIF with an HD resolution..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución HD...</value>
<comment>Converting photo to HEIF with an HD resolution...</comment>
</data>
<data name="Converting photo to HEIF with a 1440p resolution..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución 1440p...</value>
<comment>Converting photo to HEIF with a 1440p resolution...</comment>
</data>
<data name="Converting photo to HEIF with a 4K resolution..." xml:space="preserve">
<value>Convirtiendo foto a HEIF con una resolución 4K...</value>
<comment>Converting photo to HEIF with a 4K resolution...</comment>
</data>
<data name="Converting photo to AV1F with an HD resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución HD (miniatura)...</value>
<comment>Converting photo to AV1F with an HD resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to AV1F with a 1440p resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución 1440p (miniatura)...</value>
<comment>Converting photo to AV1F with a 1440p resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to AV1F with a 4K resolution (thumbnail)..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución 4K (miniatura)...</value>
<comment>Converting photo to AV1F with a 4K resolution (thumbnail)...</comment>
</data>
<data name="Converting photo to AV1F with an HD resolution..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución HD...</value>
<comment>Converting photo to AV1F with an HD resolution...</comment>
</data>
<data name="Converting photo to AV1F with a 1440p resolution..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución 1440p...</value>
<comment>Converting photo to AV1F with a 1440p resolution...</comment>
</data>
<data name="Converting photo to AV1F with a 4K resolution..." xml:space="preserve">
<value>Convirtiendo foto a AV1F con una resolución 4K...</value>
<comment>Converting photo to AV1F with a 4K resolution...</comment>
</data>
<data name="Adding photo to database..." xml:space="preserve">
<value>Añadiendo foto a la base de datos...</value>
<comment>Adding photo to database...</comment>
</data>
<data name="Please choose a valid license." xml:space="preserve">
<value>Por favor elige una licencia válida.</value>
<comment>Please choose a valid license.</comment>
</data>
<data name="Cannot run ImageMagick please contact the administrator." xml:space="preserve">
<value>No se puede ejecutar ImageMagick, por favor contacta con el administrador.</value>
<comment>Cannot run ImageMagick please contact the administrator.</comment>
</data>
<data name="The selected file is too big." xml:space="preserve">
<value>El archivo seleccionado es demasiado grande.</value>
<comment>The selected file is too big.</comment>
</data>
<data name="There was an error uploading the file." xml:space="preserve">
<value>Ocurrió un error subiendo el archivo.</value>
<comment>There was an error uploading the file.</comment>
</data>
<data name="The uploaded file was not recognized as an image." xml:space="preserve">
<value>El archivo seleccionado no se ha reconocido como una imagen.</value>
<comment>The uploaded file was not recognized as an image.</comment>
</data>
<data name="Source URL must be smaller than 255 characters." xml:space="preserve">
<value>La dirección de origen debe contener menos de 255 caracteres.</value>
<comment>Source URL must be smaller than 255 characters.</comment>
</data>
<data name="Machine photo details" xml:space="preserve">
<value>Detalles de foto de máquina</value>
<comment>Machine photo details</comment>
</data>
<data name="Machine" xml:space="preserve">
<value>Máquina</value>
<comment>Machine</comment>
</data>
<data name="Uploaded by" xml:space="preserve">
<value>Subida por</value>
<comment>Uploaded by</comment>
</data>
<data name="Uploaded date" xml:space="preserve">
<value>Subida el</value>
<comment>Uploaded date</comment>
</data>
<data name="Author" xml:space="preserve">
<value>Autor</value>
<comment>Author</comment>
</data>
<data name="Aperture" xml:space="preserve">
<value>Apertura</value>
<comment>Aperture</comment>
</data>
<data name="Please enter a valid aperture." xml:space="preserve">
<value>Por favor introduce una apertura válida.</value>
<comment>Please enter a valid aperture.</comment>
</data>
<data name="Camera manufacturer" xml:space="preserve">
<value>Fabricante de la cámara</value>
<comment>Camera manufacturer</comment>
</data>
<data name="Please enter a valid camera manufacturer." xml:space="preserve">
<value>Por favor introduce un fabricante válido.</value>
<comment>Please enter a valid camera manufacturer.</comment>
</data>
<data name="Camera model" xml:space="preserve">
<value>Modelo de la cámara</value>
<comment>Camera model</comment>
</data>
<data name="Please enter a valid camera model." xml:space="preserve">
<value>Por favor introduce un modelo válido.</value>
<comment>Please enter a valid camera model.</comment>
</data>
<data name="Color space" xml:space="preserve">
<value>Espacio de color</value>
<comment>Color space</comment>
</data>
<data name="Contrast" xml:space="preserve">
<value>Contraste</value>
<comment>Contrast</comment>
</data>
<data name="Creation date" xml:space="preserve">
<value>Fecha de creación</value>
<comment>Creation date</comment>
</data>
<data name="Please enter a correct creation date." xml:space="preserve">
<value>Por favor introduce una fecha de creación válida.</value>
<comment>Please enter a correct creation date.</comment>
</data>
<data name="Digital zoom ratio" xml:space="preserve">
<value>Relación del zoom digital</value>
<comment>Digital zoom ratio</comment>
</data>
<data name="Please enter a valid digital zoom ratio." xml:space="preserve">
<value>Por favor introduce una relación válida.</value>
<comment>Please enter a valid digital zoom ratio.</comment>
</data>
<data name="Exif version" xml:space="preserve">
<value>Versión Exif</value>
<comment>Exif version</comment>
</data>
<data name="Please enter a valid Exif version." xml:space="preserve">
<value>Por favor introduce una versión Exif válida.</value>
<comment>Please enter a valid Exif version.</comment>
</data>
<data name="Exposure time" xml:space="preserve">
<value>Tiempo de exposición</value>
<comment>Exposure time</comment>
</data>
<data name="Please enter a valid exposure time." xml:space="preserve">
<value>Por favor introduce un tiempo de exposición válido.</value>
<comment>Please enter a valid exposure time.</comment>
</data>
<data name="Exposure mode" xml:space="preserve">
<value>Modo de exposición</value>
<comment>Exposure mode</comment>
</data>
<data name="Exposure program" xml:space="preserve">
<value>Programa de exposición</value>
<comment>Exposure program</comment>
</data>
<data name="Flash" xml:space="preserve">
<value>Flash</value>
<comment>Flash</comment>
</data>
<data name="F-number" xml:space="preserve">
<value>Focal</value>
<comment>F-number</comment>
</data>
<data name="Please enter a valid focal number." xml:space="preserve">
<value>Por favor introduce un número focal válido.</value>
<comment>Please enter a valid focal number.</comment>
</data>
<data name="Focal length" xml:space="preserve">
<value>Longitud focal</value>
<comment>Focal length</comment>
</data>
<data name="Please enter a valid focal length." xml:space="preserve">
<value>Por favor introduce una longitud focal válida.</value>
<comment>Please enter a valid focal length.</comment>
</data>
<data name="Focal length in 35mm film" xml:space="preserve">
<value>Longitud focal en película de 35mm</value>
<comment>Focal length in 35mm film</comment>
</data>
<data name="Please enter a valid focal length in 35mm film." xml:space="preserve">
<value>Por favor introduce una longitud focal válida.</value>
<comment>Please enter a valid focal length in 35mm film.</comment>
</data>
<data name="Horizontal resolution" xml:space="preserve">
<value>Resolución horizontal</value>
<comment>Horizontal resolution</comment>
</data>
<data name="Please enter a valid horizontal resolution." xml:space="preserve">
<value>Por favor introduce una resolución válida.</value>
<comment>Please enter a valid horizontal resolution.</comment>
</data>
<data name="ISO rating" xml:space="preserve">
<value>Sensibilidad ISO</value>
<comment>ISO rating</comment>
</data>
<data name="Please enter a valid ISO rating." xml:space="preserve">
<value>Por favor introduce una sensibilidad válida.</value>
<comment>Please enter a valid ISO rating.</comment>
</data>
<data name="Lens" xml:space="preserve">
<value>Objectivo</value>
<comment>Lens</comment>
</data>
<data name="Please enter a valid lens." xml:space="preserve">
<value>Por favor introduce un objetivo válido.</value>
<comment>Please enter a valid lens.</comment>
</data>
<data name="Light source" xml:space="preserve">
<value>Fuente de luz</value>
<comment>Light source</comment>
</data>
<data name="Metering mode" xml:space="preserve">
<value>Modo de medición</value>
<comment>Metering mode</comment>
</data>
<data name="Resolution unit" xml:space="preserve">
<value>Unidad de resolución</value>
<comment>Resolution unit</comment>
</data>
<data name="Orientation" xml:space="preserve">
<value>Orientación</value>
<comment>Orientation</comment>
</data>
<data name="Saturation" xml:space="preserve">
<value>Saturación</value>
<comment>Saturation</comment>
</data>
<data name="Scene capture type" xml:space="preserve">
<value>Tipo de captura de escena</value>
<comment>Scene capture type</comment>
</data>
<data name="Sensing method" xml:space="preserve">
<value>Método de sensibilidad</value>
<comment>Sensing method</comment>
</data>
<data name="Sharpness" xml:space="preserve">
<value>Nitidez</value>
<comment>Sharpness</comment>
</data>
<data name="Software used" xml:space="preserve">
<value>Software utilizado</value>
<comment>Software used</comment>
</data>
<data name="Please enter a valid software used." xml:space="preserve">
<value>Por favor introduce un software utilizado válido.</value>
<comment>Please enter a valid software used.</comment>
</data>
<data name="Subject distance range" xml:space="preserve">
<value>Rango de distancia del sujeto</value>
<comment>Subject distance range</comment>
</data>
<data name="Vertical resolution" xml:space="preserve">
<value>Resolución vertical</value>
<comment>Vertical resolution</comment>
</data>
<data name="Please enter a valid vertical resolution." xml:space="preserve">
<value>Por favor introduce una resolución válida.</value>
<comment>Please enter a valid vertical resolution.</comment>
</data>
<data name="White balance" xml:space="preserve">
<value>Balance de blancos</value>
<comment>White balance</comment>
</data>
<data name="User comments" xml:space="preserve">
<value>Comentarios de usuario</value>
<comment>User comments</comment>
</data>
<data name="Please enter valid comments." xml:space="preserve">
<value>Por favor introduce unos comentarios válidos.</value>
<comment>Please enter valid comments.</comment>
</data>
<data name="Edit" xml:space="preserve">
<value>Editar</value>
<comment>Edit</comment>
</data>
<data name="Save" xml:space="preserve">
<value>Guardar</value>
<comment>Save</comment>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancelar</value>
<comment>Cancel</comment>
</data>
<data name="Back to machine" xml:space="preserve">
<value>Volver a la máquina</value>
<comment>Back to machine</comment>
</data>
<data name="Author must be 255 characters or less." xml:space="preserve">
<value>El autor debe contener 255 caracteres o menos.</value>
<comment>Author must be 255 characters or less.</comment>
</data>
<data name="Camera manufacturer must be 255 characters or less." xml:space="preserve">
<value>El fabricante de la cámara debe contener 255 caracteres o menos.</value>
<comment>Camera manufacturer must be 255 characters or less.</comment>
</data>
<data name="Camera model must be 255 characters or less." xml:space="preserve">
<value>El modelo de la cámara debe contener 255 caracteres o menos.</value>
<comment>Camera model must be 255 characters or less.</comment>
</data>
<data name="Exif version must be 255 characters or less." xml:space="preserve">
<value>La versión Exif debe contener 255 caracteres o menos.</value>
<comment>Exif version must be 255 characters or less.</comment>
</data>
<data name="Lens name must be 255 characters or less." xml:space="preserve">
<value>El nombre del objetivo debe contener 255 caracteres o menos.</value>
<comment>Lens name must be 255 characters or less.</comment>
</data>
<data name="Software used must be 255 characters or less." xml:space="preserve">
<value>El software utilizado debe contener 255 caracteres o menos.</value>
<comment>Software used must be 255 characters or less.</comment>
</data>
<data name="User comments must be 255 characters or less." xml:space="preserve">
<value>Los comentarios deben contener 255 caracteres o menos.</value>
<comment>User comments must be 255 characters or less.</comment>
</data>
</root>

View File

@@ -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<ComputersService> _l;
readonly MarechaiContext _context;
public ComputersService(MarechaiContext context, IStringLocalizer<ComputersService> localizer)
{
_context = context;
_l = localizer;
}
public ComputersService(MarechaiContext context) => _context = context;
public async Task<int> 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<List<MachineViewModel>> 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<List<MachineViewModel>> 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();
}
}

View File

@@ -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<ConsolesService> _l;
readonly MarechaiContext _context;
public ConsolesService(MarechaiContext context, IStringLocalizer<ConsolesService> localizer)
{
_context = context;
_l = localizer;
}
public ConsolesService(MarechaiContext context) => _context = context;
public async Task<int> 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<List<MachineViewModel>> 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<List<MachineViewModel>> 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();
}
}

View File

@@ -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<List<Guid>> 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<MachinePhotoViewModel> 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<Guid> 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;
}
}
}

View File

@@ -38,7 +38,8 @@ namespace Marechai.Services
public async Task<MachineViewModel> 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<CompanyLogo> logos = _context.CompanyLogos.Where(l => l.CompanyId == company.Id);

View File

@@ -71,6 +71,7 @@ namespace Marechai.Services
services.AddScoped<ResolutionsService>();
services.AddScoped<ResolutionsByScreenService>();
services.AddScoped<ResolutionsByGpuService>();
services.AddScoped<MachinePhotosService>();
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using Marechai.Database;
namespace Marechai.ViewModels
{
public class BasePhotoViewModel : BaseViewModel<Guid>
{
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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }